Copyright: Borland C++Builder by Borland International


Lexikalische Elemente

Siehe auch

Diese Themen enthalten eine formale Definition der lexikalischen Elemente von C++Builder. Sie beschreiben die wortähnlichen Einheiten einer Sprache, sogenannte Symbole oder Tokens.

Weitere Einzelheiten zu lexikalischen Elementen entnehmen Sie bitte der Themenliste unter dem Querverweis Siehe auch.

In C++Builder ergeben sich die Symbole als Folge der Operationen, die der Compiler und sein Präprozessor mit Ihrem Programm durchführen.

Ein C++Builder Programm ist eine Abfolge von ASCII-Zeichen, die den Quelltext darstellen, der mit einem Texteditor (zum Beispiel mit dem C++Builder Editor) erstellt wurde. Die grundlegende Programmeinheit in C++Builder ist die Datei. Sie entspricht einer benannten Datei im Speicher oder auf Platte und hat die Namenserweiterung C oder CPP.

Der Präprozessor durchsucht die Programme zuerst nach besonderen Präprozessor-Anweisungen (Näheres dazu unter Präprozessor-Anweisungen). So fügt beispielsweise die Direktive #include inc_file vor der Compilierung den Inhalt der Datei INC_FILE in das Programm ein. Der Präprozessor erweitert auch Makros, die im Programm und in Include-Dateien stehen.

In der Compilierungsphase der lexikalischen Analyse wird die Quelltextdatei in Symbole und Whitespace-Zeichen zerlegt.


Whitespace

Siehe auch

Whitespace-Zeichen können Leerzeichen sein, horizontale und vertikale Tabulatoren sowie Zeilenvorschübe und  Kommentare. Sie können dazu dienen, Anfang und Ende eines Symbols zu erkennen. Darüber hinaus haben Whitespace-Zeichen allerdings keine weiteren Funktionen. Beispielsweise sind

int i; float f;

und

int i;

   float f;

lexikalisch gesehen identisch und werden in dieselben sechs Symbole zerlegt:

 	int

 	i

 	;

 	float

 	f

 	;

Wenn Whitespace-Zeichen innerhalb alphanumerischer Strings stehen, werden sie vom normalen Parsing-Vorgang ausgenommen (und bleiben daher als Teil des Strings erhalten). Zum Beispiel wird:

char name[] = "Borland International";

in sieben Symbole zerlegt, einschließlich des als Einzelsymbol angesehene "Borland International".

Zeilen mit \ verbinden

Ein Sonderfall ist das Auftreten eines Backslash (\) vor dem Zeilenvorschubzeichen. Der Backslash und der Zeilenvorschub werden ignoriert, mit der Konsequenz, daß zwei physikalisch vorhandene Zeilen als eine Einheit betrachtet werden. Aus

"Borland \

International"

wird "Borland International" (siehe auch unter String-Konstanten).


Kommentare

Siehe auch

Kommentare sind Textteile, die Erklärungen zum Programm enthalten. Sie sind nur für den Programmierer von Nutzen und werden vor dem Parsen aus dem Quelltext entfernt.

Es gibt zwei Arten, Kommentare auszuweisen: die traditionelle C- und die C++ Methode. Beide werden von C++Builder unterstützt, mit der optionalen Erweiterung von verschachtelten Kommentaren. Wenn Sie nicht mit der Prämisse der ANSI-Kompatibilität compilieren, können Sie beide Methoden in C und C++ Programmen einsetzen.

Sie sollten allerdings die in der folgenden Behandlung des Themas gegebenen Empfehlungen zum Gebrauch von Whitespace und Begrenzern in Kommentaren berücksichtigen.

C-Kommentare

Ein traditioneller C-Kommentar ist eine Zeichenfolge, die nach dem Zeichenpaar /* steht. Der Kommentar endet bei dem ersten Zeichenpaar */, das auf das Startsymbol /* folgt. Die Kommentar-Begrenzungszeichen und die von ihnen eingeschlossene Zeichenfolge werden nach der Makroerweiterung durch ein Leerzeichen ersetzt. Beachten Sie, daß manche C-Implementierungen Kommentare entfernen, ohne sie durch Leerzeichen zu ersetzen.

In C++Builder ist es nicht möglich, Symbole mit Hilfe von /**/ zu verschmelzen (diese Methode ist ohnehin nicht portabel). Das Einfügen von Token wird in C++Builder mit dem ANSI-spezifizierten Zeichenpaar ## wie folgt durchgeführt:

#define VAR(i,j)  (i/**/j)    /* unzulässig */

#define VAR(i,j)  (i##j)      /* zulässig in C++Builder */

#define VAR(i,j)  (i ## j)    /* ebenfalls zulässig */

In C++Builder wird

int /* Deklaration */ i /* Zähler */;

in folgende drei Symbole zerlegt:

int   i;

Eine Beschreibung des Einfügen von Token finden Sie unter Einfügen von Token mit ##.

Kommentare in C++

In C++ können Sie einen einzeiligen Kommentar mit zwei Schrägstrichen (//) anlegen. Der Kommentar kann an beliebiger Stelle beginnen und erstreckt sich bis zur nächsten neuen Zeile:

class X {  // Kommentar

... };

Auch in reinem C-Quelltext können mit zwei Schrägstrichen Kommentare erstellt werden. Dies funktioniert allerdings nur in C++Builder.

Verschachtelte Kommentare

In ANSI C sind verschachtelte Kommentare nicht zulässig. Wenn Sie die vorausgehende Zeile folgendermaßen herauskommentieren wollen:

/*  int /* Deklaration */ i /* Zähler */; */

tritt ein Fehler auf, weil der Bereich des ersten /* am ersten */ endet. Dies ergibt

i ; */

wodurch ein Syntaxfehler verursacht wird.

Per Voreinstellung verbietet C++Builder verschachtelte Kommentare. Diese Einstellung kann jedoch mit Hilfe von Compiler-Optionen geändert werden.

Begrenzer und Whitespace

In seltenen Fällen können durch Verwendung von (syntaktisch nicht erforderlichen) Whitespace-Zeichen vor /* bzw. // und nach */ Portabilitätsprobleme vermieden werden. Beispielsweise wird der C++ Quelltext

int i = j//* dividiert durch k*/k;

+m;

zerlegt in

int i = j + m
und nicht in:

int i = j/k;

+m;

wie es normalerweise in C der Fall wäre. Dieses Problem läßt sich durch die folgende, besser lesbare Schreibweise vermeiden:

int i = j/ /* dividiert durch k*/ k;

+m;


Symbole

Siehe auch

Unter Symbolen (Token) werden hier die wortähnlichen Einheiten einer Programmiersprache verstanden. C++Builder kennt sechs Arten von Symbolen.

Hier ist die formale Definition eines Symbols:

 Schlüsselwort

 Bezeichner

 Konstante

 String-Literal

 Operator

 Interpunktionszeichen (auch als Trennzeichen oder Separatoren bezeichnet

Beim Durchsuchen des Quelltextes wird aus einer gegebenen Zeichenfolge zuerst das längstmögliche Symbol gebildet. Dadurch wird etwa external als Bezeichner ermittelt und nicht in das Schlüsselwort extern und den Bezeichner al zerlegt.

Eine Beschreibung des Einfügen von Token finden Sie unter Einfügen von Token mit ##.


Schlüsselwörter

Siehe auch

Schlüsselwörter sind für spezielle Zwecke reservierte Wörter, die nicht als normale Bezeichnernamen verwendet werden dürfen. Eine Übersicht finden Sie in den folgenden Tabellen:

Alphabetische Liste der Schlüsselwörter.

Tabelle der C++ Schlüsselwörter

Tabelle der C++Builder Register-Pseudovariablen

Wenn Sie nicht-ANSI-Schlüsselwörter in einem Programm verwenden und es ANSI-verträglich machen wollen, dann stellen Sie bei allen nicht-ANSI-Schlüsselwortversionen einen doppelten Unterstrich voran. Einige Schlüsselwörter besitzen eine Version mit nur einem vorangestellten Unterstrich. Diese Schlüsselwörter werden zur Verfügung gestellt, um die Portierung eines Quelltextes, der mit anderen Compilern entwickelt wurde, zu erleichtern. Für die von ANSI spezifizierten Schlüsselwörter gibt es nur eine Version.

Hinweis:	Beachten Sie, daß die Schlüsselwörter _ _try und try eine Ausnahme zur oben stehenden Diskussion darstellen. Das Schlüsselwort try wird in Verbindung mit dem Schlüsselwort catch beim Exception-Behandlungsmechanismus von C++ gefordert. try kann nicht durch _ _try ersetzt werden. Das Schlüsselwort _ _try kann nur in Verbindung mit den Schlüsselwörtern _ _except und _ _finally verwendet werden. Siehe dazu auch die Diskussion über Exception-Behandlung unter Exception Handling in C++ und  Auf C-Basis strukturierte Exceptions.


Tabelle der C++ spezifischen Schlüsselwörter

Es gibt einige spezielle C++ Schlüsselwörter. Sie sind nicht verfügbar, wenn Sie ein Programm „nur“ in C schreiben.

Die C++ spezifischen Schlüsselwörter sind:

asm	mutable	this

bool	namespace	throw

catch	new	true

class	operator	try

const_cast	private	typeid

delete	explicit	protected	reinterpret_cast

dynamic_cast	public	using

false	__rtti	virtual

friend	static_cast	wchar_t

inline	template	typename


Tabelle der Borland C++ Register-Pseudovariablen
Tabelle der C++Builder Register-Pseudovariablen

Siehe auch

_AH	_CL	_EAX1	_ESP
_AL	_CS	_EBP1	_FLAGS
_AX	_CX	_EBX1	_FS
_BH	_DH	_ECX1	_GS1
_BL	_DI	_EDI1	_SI
_BP	_DL	_EDX1	_SP
_BX	_DS	_ES	_SS
_CH	_DX	_ESI1
1	Diese Pseudovariablen stehen für den 32-Bit-Compiler immer zur Verfügung. Der 16-Bit-Compiler kann sie nur benutzen, wenn Sie die Option zum Generieren von 80386-Befehlen verwenden.

Alle außer der _FLAGS Register-Pseudovariablen werden mit den allgemeinen Registern, Segment-, Adress- und speziellen Registern verknüpft.

Verwenden Sie Register-Pseudovariablen überall, wo Sie eine Integer-Variable für den direkten Zugriff auf das entsprechende 80x86 Register verwenden können.

Das 16-Bit Flags-Register enthält Informationen zum Status des 80x86 und die Ergebnisse aktueller Instruktionen.


C++Builder zusätzliche Schlüsselwörter

C++Builder stellt zusätzliche Schlüsselwörter zur Verfügung, die nicht zum ANSI-Standard gehören.

Die zusätzlichen Schlüsselwörter von C++Builder sind:

_asm

__asm

__automated

_cdecl

cdecl

__classid

__closure

__declspec

__except

__export

_export

__fastcall

_fastcall

__finally

_import

__import

__int8

__int16

__int32

__int64

_pascal

__pascal

pascal

__property

__published

__rtti

__thread

__try


Bezeichner

Siehe auch

 Dies die formale Definition für einen Bezeichner:

Bezeichner:

Nicht-Ziffer

Bezeichner Nicht-Ziffer

Bezeichner Ziffer

Nicht-Ziffer: eines der folgenden Zeichen:

a b c d e f g h i j k l m n o p q r s t u v w x y z _

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

Ziffer: eines der folgenden Zeichen:

0  1  2  3  4  5  6  7  8  9

Namen- und Längenbeschränkungen

Bezeichner sind beliebige Namen von beliebiger Länge für Klassen, Objekte, Funktionen, Variablen, benutzerdefinierte Datentypen, etc. Bezeichner können die Buchstaben a bis z, A bis Z, den Unterstrich »_« und die Ziffern 0 bis 9 enthalten. Es gibt jedoch zwei Einschränkungen:

 Das erste Zeichen muß ein Buchstabe oder der Unterstrich sein.

 Für Bezeichner in C++ Programmen sind standardmäßig die ersten 32 Zeichen signifikant. Die Anzahl der signifikanten Zeichen kann durch Menüpunkte und Kommandozeilenparameter zwar reduziert, aber nicht erhöht werden

Groß- und Kleinschreibung

In C++Builder wird zwischen Groß- und Kleinschreibung unterschieden, so daß Sum, sum und suM unterschiedliche Bezeichner sind.

Für globale Bezeichner aus anderen Modulen gelten dieselben Regeln in Bezug auf Namengebung und signifikante Zeichenanzahl wie für normale Bezeichner. Die Berücksichtigung der Groß- und Kleinschreibung kann in C++Builder jedoch außer Kraft gesetzt werden, um Kompatibilität zu ermöglichen, wenn man mit Sprachen linkt, die Groß- und Kleinschreibung nicht beachten. Mit der Option, die Groß- und Kleinschreibung nicht zu beachten, werden globales Sum und sum als identische Bezeichner betrachtet. Dies kann beim Linken allerdings die Warnung "Doppeltes Symbol" zur Folge haben.

Eine Ausnahme von dieser Regel bilden Bezeichner vom Typ _ _pascal, die für das Linken immer in Großbuchstaben umgewandelt werden.

Eindeutigkeit und Gültigkeitsbereich von Bezeichnern

Bezeichner können beliebige Namen haben, die den geltenden Regeln entsprechen. Es kann jedoch zu Fehlern kommen, wenn derselbe Name innerhalb desselben Gültigkeitsbereichs für mehrere Bezeichner verwendet wird, die denselben Namensbereich haben. Gleiche Namen sind für verschiedene Namensbereiche zulässig, unabhängig vom Gültigkeitsbereich.


Konstanten

Siehe auch

Konstanten sind Symbole, die für feste numerische oder Zeichenwerte stehen.

C++Builder kennt vier Klassen von Konstanten: Integer, Gleitkomma, Zeichen (einschließlich Strings) und Aufzählung.

Die Abbildung Interne Darstellung der numerischen Datentypen verdeutlicht die interne Repräsentation dieser Typen.

Der Datentyp einer Konstante wird vom Compiler auf Grund ihres numerischen Wertes und ihres Formats im Quelltext abgeleitet. Die formale Definition einer Konstante demonstriert die folgende Tabelle:

Konstanten - formale Definition

Konstante:	Ziffern ohne Null: jeweils aus der Menge

   Gleitkomma-Konstante	   1  2  3  4  5  6  7  8  9

   Integer-Konstante

   Aufzählungs-Konstante

   Zeichen-Konstante

Gleitkomma-Konstante:	Oktale Ziffer: jeweils aus der Menge

   Mantisse <Exponent> <Gleitkomma-Suffix>	   0  1  2  3  4  5  6  7

   Ziffernfolge Exponent <Gleitkomma-Suffix>

Mantisse:	Hexadezimale Ziffer: jeweils aus der Menge

   <Ziffernfolge>  .  Ziffernfolge	   0  1  2  3  4  5  6  7  8  9

   Ziffernfolge  .	   a  b  c  d  e  f

   A  B  C  D  E  F

Exponent:	integer-Suffix:

   e  <Vorzeichen> Ziffernfolge	unsigned-Suffix  <long-Suffix>

   E  <Vorzeichen> Ziffernfolge	long-Suffix  <unsigned-Suffix>

Vorzeichen: jeweils aus der Menge	unsigned-Suffix: jeweils aus der Menge

   +  -	u  U

Ziffernfolge:	long-Suffix: jeweils aus der Menge

   digit	l  L

   Ziffernfolge  digit

Gleitkomma-Suffix: jeweils aus der Menge	Aufzählungs-Konstante:

   f   l   F   L	Bezeichner

Integer-Konstante:	character-Konstante

   Dezimale Konstante  <integer-Suffix>	c-char-Folge

   Oktale Konstante  <integer-Suffix>

   Hexadezimale Konstante  <integer-Suffix>

Dezimale Konstante:	c-char-Folge:

   Ziffern ohne Null	c-char

   Dezimale Konstante  Ziffer	c-char-Folge  c-char

Oktale Konstante:	c-char:

   0	irgendein Zeichen des geg. Zeichensatzes

   Oktale Konstante  Oktale Ziffer	außer einfachen Anführungszeichen ('), Backslash (\), oder

Zeilenvorschub Escape-Sequenzen.

Hexadezimale Konstante:	Escape-Sequenz: jeweils aus der Menge

   0 x Hexadezimale Ziffer	\"          \'           \?          \\

   0 X Hexadezimale Ziffer	\a          \b          \f          \n

   Hexadezimale Konstante  Hexadezimale Ziffer	\o          \oo        \ooo    \r

\t           \v          \Xh...    \xh...


Integer-Konstanten

Siehe auch

Integerkonstanten können dezimale (Basis 10), oktale (Basis 8) oder hexadezimale (Basis 16) Zahlen sein. Sind keine Suffixe vorhanden, wird der Datentyp einer Integerkonstanten von ihrem Wert abgeleitet (vergleichen Sie C++Builder Integerkonstanten ohne L- oder U-Suffix.). Beachten Sie, daß für dezimale und nichtdezimale Konstanten unterschiedliche Regeln gelten.

Dezimalkonstanten

Es sind Dezimalkonstanten von 0 bis 4294967295 zugelassen. Konstanten außerhalb dieses Bereichs werden entsprechend abgeschnitten. Dezimalkonstanten dürfen nicht mit einer Null (0) beginnen, ansonsten werden sie als oktale Konstanten interpretiert:

int i = 10;  /*dezimal 10 */

int i = 010; /*dezimal 8 */

int i = 0;   /*dezimal 0 = oktal 0 */

Oktale Konstanten

Alle Konstanten, die mit einer Null beginnen, werden als oktale Konstanten interpretiert. Enthält eine oktale Konstante die unzulässigen Ziffern 8 oder 9, wird ein Fehler gemeldet. Oktale Konstanten werden abgeschnitten, wenn sie größer als 037777777777 sind.

Hexadezimale Konstanten

Alle Konstanten, die mit 0x (oder 0X) beginnen, werden als hexadezimale Konstanten interpretiert. Hexadezimale Konstanten werden abgeschnitten, wenn sie größer als 0xFFFFFFFF sind.

long- und unsigned- Suffixe

Beinhaltet eine Konstante das Suffix L (oder l), wird die Konstante als long dargestellt. Das Suffix U (oder u) bedingt eine unsigned (vorzeichenlose) Konstante. Eine Konstante ist unsigned long, wenn ihr Wert größer als dezimal 65535 ist, unabhängig von der Basis. Die Suffixe L und U können in beliebiger Reihenfolge und Schreibweise an eine Konstante angefügt werden: ul, lu, UL, etc.

Ist kein U-, u-, L- oder l-Suffix vorhanden, entspricht der Datentyp einer Konstanten dem ersten zu ihrem Wert passenden Typ:

Dezimal	int, long int, unsigned long int
Oktal	int, unsigned int, long int, unsigned long int
Hexadezimal	int, unsigned int, long int, unsigned long int
Hat die Konstante das Suffix U oder u, entspricht der Datentyp dem ersten unsigned int bzw. unsigned long int, je nachdem, was zum Wert paßt.

Hat die Konstante das Suffix L oder l, entspricht der Datentyp dem ersten long int bzw. unsigned long int, je nachdem, was zum Wert paßt.

Hat die Konstante beide Suffixe (ul, lu, UI, lU, uL, Lu, LU oder UL), ist sie vom Datentyp unsigned long int.

C++Builder Integerkonstanten ohne L- oder U-Suffix faßt die Repräsentation von Integerkonstanten in allen drei Basen zusammen. Bei der Angabe der Datentypen wird davon ausgegangen, daß kein L- oder U- Suffix eingesetzt wurde.


Erweiterte Integertypen

Siehe auch

Sie können die Größe von Integertypen angeben. Sie müssen den entsprechenden Suffix verwenden, wenn Sie erweiterte Integertypen einsetzen.

Typ	Suffix	Beispiel	Speicherung

__int8	i8	__int8 c = 127i8;	8 Bits

__int16	i16	__int16 s = 32767i16;	16 Bits

__int32	i32	 __int32 i = 123456789i32;	32 Bits

__int64	i64	__int64 big = 12345654321i64;	64 Bits

unsigned __int64	ui64	unsigned __int64  hugeInt = 	64 Bits

     1234567887654321ui64;


Borland C++ Integer-Konstanten ohne L- oder U-Suffix
C++Builder Integerkonstanten ohne L- oder U-Suffix

Siehe auch

Dezimalkonstanten

0	to	32,767	int

32,768	to	2,147,483,647	long

2,147,483,648	to	4,294,967,295	unsigned long

> 4294967295	wird abgeschnitten

Oktale Konstanten

00	to	077777	int

010000	to	0177777	unsigned int

02000000	to	017777777777	long

020000000000	to	037777777777	unsigned long

> 037777777777	wird abgeschnitten

Hexadezimale Konstanten

0x0000	to	0x7FFF	int

0x8000	to	0xFFFF	unsigned int

0x10000	to	0x7FFFFFFF	long

0x80000000	to	0xFFFFFFFF	unsigned long

>0xFFFFFFFF	wird abgeschnitten


Gleitkommakonstanten

Siehe auch

 Eine Gleitkommakonstante setzt sich aus fünf Bestandteilen zusammen:

 Vorkommastellen

 Dezimalpunkt

 Nachkommastellen

 e oder E und ein vorzeichenbehafteter Integerexponent (optional)

 Suffix: f, F oder l, L (optional)

Sie können entweder die Vorkomma- oder Nachkommastellen wegfallen lassen (aber nicht beide). Sie können entweder den Dezimalpunkt oder den Buchstaben e (E) und den Exponenten weglassen (aber nicht beide). Diese Regeln ermöglichen sowohl die konventionelle als auch die wissenschaftliche Notation (mit Exponenten).

Negative Gleitkommakonstanten werden als positive Konstanten mit vorangestelltem unären Minusoperator behandelt.

Dazu einige Beispiele:

Konstante	Wert

23.45e6	23.45  106

.0	0

0.	0

1.	1.0  100 = 1.0

-1.23	-1.23

2e-5	2.0  10-5

3E+10	3.0  1010

.09E34	0.09  1034

Gleitkommakonstanten ohne Suffix sind vom Datentyp double. Durch Anfügen eines f- oder F-Suffixes können Sie jedoch einer Gleitkommakonstanten den Typ float, durch Anfügen eines l- oder L-Suffixes den Typ long double zuweisen. In der folgenden Tabelle sehen Sie die Bereiche für float, double und long double.

Größe und Bereich der C++Builder Gleitkommakonstanten

Typ	Größe (Bits)	Bereich

float	32	3.4   10-38 bis 3.4  1038

double	64	1.7  10-308 bis 1.7  10308

long double	80	3.4  10-4932 bis 1.1  104932


Zeichenkonstanten

Siehe auch

Eine Zeichenkonstante besteht aus einem oder mehreren in Apostrophen eingeschlossenen Zeichen, wie etwa `A`, `=` oder `\n`. In C ist eine Zeichenkonstante vom Datentyp int. Die Zahl von Bits für die interne Repräsentation einer Zeichenkonstante ist sizeof(int). Bei einem 16-Bit-Programm enthält das höherwertige Byte den Wert 0 oder das Vorzeichen. In C++ hat eine Zeichenkonstante den Datentyp char. Konstanten mit mehreren Zeichen haben in C und C++ den Typ int.

Um mehr über Zeichenkonstanten zu erfahren, schauen Sie bitte unter folgenden Themen nach:

 Die drei char-Typen

 Escape-Sequenzen

 Wide-Character- und Mehr-Zeichenkonstanten

Hinweis:	Compilieren Sie dies für einen Vergleich der Zeichentypgrößen zuerst als ein C-Programm und dann als ein C++ Programm.

#include <stdio.h>

#define CH 'x'       /* EINE ZEICHENKONSTANTE */

void main(void) {

   char ch = 'x';    /* EINE ZEICHENVARIABLE      */

   printf("\nSizeof int     = %d", sizeof(int) );

   printf("\nSizeof char    = %d", sizeof(char) );

   printf("\nSizeof ch      = %d", sizeof(ch) );

   printf("\nSizeof CH      = %d", sizeof(CH) );

   printf("\nSizeof wchar_t = %d", sizeof(wchar_t) );

}

Hinweis:	Größenangaben beziehen sich auf Bytes.

Größen von Zeichentypen

Ausgabe, wenn als C Programm kompiliert	Ausgabe, wenn als C++ Programm kompiliert

Sizeof int	=	4	Sizeof int	=	4

Sizeof char	=	1	Sizeof char	=	1

Sizeof ch	=	1	Sizeof ch 	=	1

Sizeof CH	=	4	Sizeof CH	=	1

Sizeof wchar_t	=	2	Sizeof wchar_t	=	2


Die drei Zeichentypen
Die drei char-Typen

Siehe auch

Konstanten, die aus einem einzigen Zeichen bestehen (beispielsweise  `A`, `\t` und `\007`), werden als int dargestellt. In diesem Fall gilt das niederwertige Bit als vorzeichenbehaftet, das heißt, wenn der Wert größer ist als 127 (Basis 10), wird das höherwertige Bit auf -1 (hexadezimal: 0xFF) gesetzt. Diese Automatik kann ausgeschaltet werden, indem man als standardmäßigen char-Typ unsigned char wählt, wodurch das höherwertige Bit dann unabhängig vom niederwertigen Bit immer auf 0 gesetzt wird.

Die drei Zeichentypen char, signed char und unsigned char erfordern einen 8-Bit-Speicher (ein Byte). In C- und C++Builder Programmen vor der Version C++Builder 4.0 wurde char genauso wie signed char behandelt. Das Verhalten von C-Programmen bleibt durch die Unterscheidung zwischen diesen drei Zeichentypen unberührt.

Hinweis:	Um das alte Verhalten wieder zu erlangen, benutzen Sie bitte die Option K2 auf der Befehlszeilenebene und die C++Builder 3.1 Headerdateien und Bibliotheken.

In einem C++ Programm kann eine Funktion mit Argumenten vom Typ char, signed char oder unsigned char überladen werden. Die folgenden Funktionsprototypen sind zum Beispiel gültig und verschieden.

void func(char ch);

void func(signed char ch);

void func(unsigned char ch);

Wenn nur einer der oberen Prototypen existiert, wird er jeden der drei Zeichentypen akzeptieren. Das folgende ist zum Beispiel möglich:

void func(unsigned char ch);

void main(void) {

  signed char ch = 'x';

  func(ch);

  }


Escape-Sequenzen

Siehe auch

Mit dem Backslash (\) beginnt eine Escape-Sequenz, die zur Repräsentation nicht darstellbarer Zeichen dient (z.B. steht \n für das Zeichen "Zeilenvorschub".

Ein Backslash in Kombination mit oktalen oder hexadezimalen Zahlen repräsentiert den ASCII- oder Steuercode, der diesem Wert entspricht, z.B. '\03' für Ctrl-C oder '\x3F' für das Fragezeichen. Eine Escape-Sequenz kann einen String mit bis zu drei oktalen oder beliebig vielen hexadezimalen Ziffern umfassen, vorausgesetzt, der Wert liegt im zulässigen Bereich des Datentyps char (0 bis 0xff in C++Builder). Größere Zahlen liefern den Compilerfehler Numerische Konstante zu groß. Die Oktalzahl \777 ist zum Beispiel größer als der zulässige Höchstwert \377 und ergibt somit einen Fehler. Das erste nicht-oktale oder nicht-hexadezimale Zeichen in einer oktalen oder hexadezimalen Escape-Sequenz markiert das Ende der Sequenz.

Sehen Sie dieses Beispiel:

printf("\x0072.1A Simple Operating System");

Diese Zeile soll als \x007 und "2.1A Simple Operating System" interpretiert werden. In C++Builder wird \x0072 jedoch als hexadezimale Zahl und "2.1A Simple Operating System" als literaler String interpretiert.

Zur Umgehung derartiger Probleme sollte die Programmzeile folgendermaßen neu geschrieben werden:

printf("\x007" "2.1A Simple Operating System");

Mehrdeutigkeiten entstehen auch, wenn auf eine oktale Escape-Sequenz eine nicht-oktale Ziffer folgt. Da 8 und 9 unzulässige oktale Ziffern sind, würde z.B. die Konstante \258 als Zwei-Zeichen-Konstante interpretiert, die sich aus den zwei Zeichen \25 und 8 zusammensetzt.

In der folgenden Tabelle sind alle Escape-Sequenzen zusammengefaßt.

C++Builder Escape-Sequenzen

Hinweis:	Sie müssen den (typischerweise in Pfadnamen verwendeten) Backslash verdoppeln (also "\\" angeben), um ihn als ANSI-Backslash gelten zu lassen.

Sequenz	Wert	Zeichen	Bedeutung

\a	0x07	BEL	Alarmton

\b	0x08	BS	Backspace

\f	0x0C	FF	Seitenvorschub

\n	0x0A	LF	Zeilenvorschub

\r	0x0D	CR	Wagenrücklauf (Carriage Return)

\t	0x09	HT	Tabulator(horizontal)

\v	0x0B	VT	Tabulator (vertikal)

\\	0x5c	\	Backslash (umgekehrter Schrägstrich)

\'	0x27	'	Einfaches Anführungszeichen (Apostroph)

\"	0x22	"	Doppeltes Anführungszeichen

\?	0x3F	?	Fragezeichen

\O	beliebig	O = ein String mit 1 bis 3 Oktalziffern

\xH	beliebig	H = ein String mit Hex-Ziffern

\XH	beliebig	H = ein String mit Hex-Ziffern


Wide-Character- und Mehr-Zeichenkonstanten
Wide-Character-Konstanten und Mehr-Zeichenkonstanten

Siehe auch

"Wide-Character"-Typen können verwendet werden, um ein Zeichen zu repräsentieren, das nicht in den Speicherplatz paßt, der für einen char-Typ reserviert wird. Ein Wide-Character benötigt einen Speicherplatz von zwei Bytes. Eine Zeichenkonstante, der ein L unmittelbar vorangeht, ist eine Wide-Character-Konstante vom Datentyp wchar_t (in stddef.h definiert). Ein Beispiel:

wchar_t ch = L'AB';

Falls wchar_t in einem C Programm verwendet wird, stellt es einen in der Headerdatei stdde.h definierten Typ dar. In einem C++ Programm ist wchar_t ein Schlüsselwort zur Darstellung unterschiedlicher Codes beliebiger Elemente der erweiterten landesspezifischen Zeichensätze. In C++ hat wchar_t die selbe Größe, die selbe Vorzeichenbehandlung und Ausrichtung wie ein int-Typ.

Ein String, dem ein L unmittelbar vorangeht, ist ein Wide-Character-String. Die Speicherreservierung für einen solchen String liegt bei zwei Bytes pro Zeichen. Ein Beispiel:

wchar_t str = L"ABCD";

Mehr-Zeichenkonstanten

C++Builder unterstützt ebenso Mehr-Zeichenkonstanten. Mehr-Zeichenkonstanten können bis zu vier Zeichen umfassen. So ist zum Beispiel die Konstante '\006\007\008\009' ist nur in einem C++Builder Programm gültig. Mehr-Zeichenkonstanten sind immer 32-Bit Int-Werte. Die Konstanten sind (deshalb) nicht portabel zu anderen C Compilern.


String-Konstanten

Siehe auch

String-Konstanten (oder String-Literale) sind eine spezielle Konstantenkategorie für feste Zeichenfolgen. Eine String-Konstante ist vom Datentyp array-of-char und gehört zur Speicherklasse static. Derartige Konstanten setzen sich aus einer Reihe beliebig vieler Zeichen in Anführungszeichen zusammen:

"This is literally a string!"

Der Null-String (leerer String) ist: "".

Die Zeichen, die innerhalb der Anführungszeichen stehen, können auch Escape-Sequenzen enthalten. Der Quelltext

"\t\t\"Name\"\\\tAddress\n\n"

gibt folgendes aus:

"Name"\       Address

Vor "Name" stehen zwei Tabulatoren, vor Adresse ein Tabulator. Danach folgen zwei neue Zeilen. \" sorgt für die Ausgabe der Anführungszeichen im String.

Die Escape-Zeichensequenz "\\" wird von dem Compiler in "\" übersetzt, wenn Sie auf der Seite Compiler/Quelltext im Dialogfenster Projektoptionen unter Sprachumfang gemäß die Option ANSI auswählen.

String-Konstanten werden intern als die gegebene Zeichensequenz plus einem abschließenden Null-Zeichen ('\0') abgespeichert. Ein leerer String wird als ein '\0'-Zeichen abgelegt.

String-Konstanten, die nur durch Whitespace getrennt sind, werden während des Parsens verkettet. Das Programm

#include <stdio.h>

int main() {

   char    *p;

   _InitEasyWin();

   p = "Dies ist ein Beispiel dafür,  wie C++Builder"
     " automatisch die Verkettung sehr langer"
     " Strings durchführt. Dadurch wird Ihr Programm"
     " übersichtlicher gestaltet.";

   printf(p);

   return(0);

}

liefert folgenden Ausdruck:

Dies ist ein Beispiel dafür,  wie C++Builder automatisch die

Verkettung sehr langer Strings durchführt. Dadurch wird Ihr

Programm übersichtlicher gestaltet.";

Durch Verwendung des Backslash (\) können Sie eine String-Konstante auf zwei Zeilen aufteilen:

puts("Dies ist in Wirklichkeit\

ein einzeiliger String");


Aufzählungstyp-Konstanten

Siehe auch

Aufzählungstyp-Konstanten sind Bezeichner, die in enum-Deklarationen definiert werden. Diese Bezeichner werden normalerweise unter mnemotechnischen Aspekten gewählt. Die Aufzählungstyp-Konstanten sind Integerdatentypen. Sie können in allen Ausdrücken eingesetzt werden, in denen Integerkonstanten zulässig sind. Derselbe Bezeichner darf im Gültigkeitsbereich der enum-Deklaration nur einmal vorkommen. Negative Initialisierer sind erlaubt. Eine detaillierte Erläuterung finden Sie unter Aufzählungen und enum (Schlüsselwort).

Der von Aufzählungstyp-Konstanten übernommene Wert richtet sich nach der enum-Deklaration und dem Vorhandensein von (optionalen) Initialisierern. In dem Beispiel

enum team { giants, cubs, dodgers };

sind giants, cubs und dodgers Aufzählungstyp-Konstanten vom Typ team, die jeder Variablen vom Typ team oder jeder anderen Variablen eines Integertyps zugewiesen werden können. Die Werte, die die Aufzählungstyp-Konstanten annehmen können, lauten:

giants = 0, cubs = 1, dodgers = 2

In dem Beispiel (ohne explizite Initialisierer)

enum team { giants, cubs=3, dodgers = giants + 1 };

sind die Konstanten folgendermaßen gesetzt:

giants = 0, cubs = 3, dodgers = 1

Die Konstantenwerte müssen nicht eindeutig sein:

enum team { giants, cubs = 1, dodgers = cubs - 1 };


Konstanten und ihre interne Darstellung

Siehe auch

ANSI C "weiß", daß die Größe und der numerische Bereich der grundlegenden Datentypen (und ihrer verschiedenen Varianten) implementierungsspezifisch sind und sich normalerweise von der Architektur des Rechners ableiten. Für C++Builder ist die IBM-PC-Familie (und alle kompatiblen Rechner) der Ausgangspunkt. Somit bestimmt die Architektur der Intel 8088- und 80x86-Mikroprozessoren die Auswahl der internen Darstellung für die verschiedenen Datentypen.

Die folgenden Tabellen enthalten die Größen und die sich daraus ergebenden Bereiche für die grundlegenden Datentypen in C++Builder.Interne Darstellung numerischer Datentypen zeigt die interne Darstellung der Datentypen.

32-Bit Datentypen, Größen und Bereiche

Typ	Größe (Bits)	Bereich	Beispielanwendungen

unsigned char	8	0 to 255	Kleine Zahlen und kompletter PC-Zeichensatz

char	8	-128 to 127	Sehr kleine Zahlen und ASCII-Zeichen

short int	16	-32,768 to 32,767	Zähler, kleine Zahlen, Schleifensteuerung

unsigned int	32	0 to 4,294,967,295	Große Zahlen und Schleifen

int	32	-2,147,483,648 to 2,147,483,647	Zähler, kleine Zahlen, Schleifensteuerung

unsigned long	32	0 to 4,294,967,295	Astronomische Distanzen

enum	32	-2,147,483,648 to 2,147,483,647	Geordnete Wertemengen

long	32	-2,147,483,648 to 2,147,483,647	Sehr große Zahlen, statistische Grundgesamtheiten

float	32	3.4 ^ 10-38 to 1.7 ^ 1038	Wissenschaftlich, 7-stellige Genauigkeit

double	64	1.7 ^ 10-308 to 3.4 ^ 10308	Wissenschaftlich, 15-stellige Genauigkeit

long double	80	3.4 ^ 10-4932 to 1.1 ^ 104932	Finanzrechnung, 18-stellige Genauigkeit


Datentypen (32-Bit)

Siehe auch	Schlüsselwörter

Typ	Länge	Bereich

unsigned char 	8 bits	0  bis  255

char	8 bits	-128  bis  127

short int	16 bits	-32,768  bis  32,767

unsigned int	32 bits	0  bis  4,294,967,295

int	32 bits	-2,147,483,648  bis  2,147,483,647

unsigned long 	32 bits	0  bis  4,294,967,295

enum	16 bits	-2,147,483,648  bis  2,147,483,647

long	32 bits	-2,147,483,648  bis  2,147,483,647

float	32 bits	3.4 x 10-38  bis  3.4 x 10+38

double	64 bits	1.7 x 10-308  bis  1.7 x 10+308

long double	80 bits	3.4 x 10-4932  bis  1.1 x 10+4932


Interne Darstellung numerischer Typen
Interne Darstellung numerischer Datentypen

Siehe auch

32-Bit Integer

Gleitkommazahl-Typen, immer

s	=	Vorzeichen Bit ( 0 = positiv, 1 = negativ)	Exponent bias (normalisierte Werte):
I	=	Position des impliziten binär Punktes	float: 127 (7FH)
1	=	Signifikantes Integer Bit:	double: 1,023 (3FFH)
		Gespeichert in long double Implizit in float, double	long double: 16,383 (3FFFH)


Konstantenausdrücke

Siehe auch

Ein Konstantenausdruck ist ein Ausdruck, der zu einer Konstante ausgewertet wird. Er muß einen Wert ergeben, der im Wertebereich seines Typs liegt. Konstantenausdrücke werden genauso ausgewertet wie reguläre Ausdrücke. Konstantenausdrücke können überall dort verwendet werden, wo eine Konstante erlaubt ist. Die Syntax für einen Konstantenausdruck lautet:

Konstantenausdruck.

Bedingter Ausdruck

Konstantenausdrücke dürfen keinen der folgenden Operatoren enthalten, es sei denn, daß die Operatoren Operanden des sizeof-Operators sind:

 Zuweisungsoperator

 Komma-Operator

 Dekrement-Operator

 Funktionsaufruf

 Inkrement-Operator


Interpunktionszeichen

Siehe auch

Interpunktionszeichen (auch Trennzeichen genannt) sind in C++Builder wie folgt definiert:

[ ]

( )

{ }

,

;

:

...

*

=

#

Die meisten dieser Interpunktionszeichen sind ebenfalls Operatoren.

Eckige Klammern

Öffnende und schließende eckige Klammern [ ] beinhalten einfache und mehrdimensionale Array-Indizes:

char ch, str[] = "Stan";

int mat[3][4];           /* 3 x 4 matrix */

ch = str[3];             /* 4th element */

  .

  .

  .

Runde Klammern

Öffnende und schließende runde Klammern ( ) fassen Ausdrücke zusammen, isolieren konditionale Ausdrücke und repräsentieren Funktionsaufrufe und Funktionsparameter:

d = c * (a + b);	/* Ändern der normalen Reihenfolge */

if (d == z) ++x;	/* Erforderlich bei einer bedingten Anweisung */

func();	/* Funktionsaufruf ohne Argumente */

int (*fptr)();	/* Funktionszeiger-Deklaration */

fptr = func;	/* keine () bedeutet Funktionszeiger */

void func2(int n);	/* Funktionsdeklaration mit Argumenten */

Runde Klammern müssen in Makrodefinitionen verwendet werden, damit potentielle Rangfolgeprobleme bei der Erweiterung ausgeschlossen sind:

#define CUBE(x) ((x) * (x) * (x))

Der Einsatz von runden Klammern zur Änderung der normalen Operator-Rangfolge und der Assoziativitätsregeln werden unter dem Thema Ausdrücke erläutert.

Geschweifte Klammern

Öffnende und schließende geschweifte Klammern { } markieren den Beginn und das Ende eines Anweisungsblocks:

if (d == z)

{

   ++x;

   func();

}

Die schließende Klammer dient als Endekriterium für den Anweisungsblock; aus diesem Grund darf nach } kein Semikolon (;) mehr stehen, außer bei Struktur- oder Klassendeklarationen. Häufig ist ein Semikolon unzulässig, wie z.B.

if (Ausdruck)

   {};                  /*unzulässiges Semikolon*/

else

Komma

Das Komma (,) trennt die Elemente in einer Funktions-Argumentliste:

void func(int n, float f, char ch);

Das Komma wird auch als Operator in Komma-Ausdrücken verwendet. Die Kombination beider Komma-Anwendungen ist erlaubt, wenn gleichzeitig runde Klammern zur Unterscheidung eingesetzt werden:

func(i, j);                              /* Funktionsaufruf mit zwei Argumenten */

func((exp1, exp2), (exp3, exp4, exp5));  /* auch Funktionsaufruf mit zwei
                                       Argumenten! */

Semikolon

Das Semikolon (;) dient als Endekriterium einer Anweisung. Jeder gültige C- oder C++ Ausdruck (auch der leere Ausdruck), an dessen Ende ein Semikolon steht, wird als Anweisung (Ausdruck-Anweisung) interpretiert. Der Ausdruck wird nur ausgewertet und sein Wert wird verworfen. Wenn der Ausdruck keine Nebeneffekte hat, wird er möglicherweise von C++Builder ignoriert.

a + b;    /* a + b wird ausgewertet, aber der Wert wird verworfen */

++a;      /* Nebeneffekt auf a, aber der Wert von ++a wird verworfen */

;         /* leerer Ausdruck = leere Anweisung */

Semikola werden oft zur Erstellung einer leeren Anweisung verwendet:

for (i = 0; i < n; i++)

{

   ;

}

Doppelpunkt

Mit dem Doppelpunkt (:) wird ein Label gekennzeichnet.

start:    x=0;
   ƒ

goto start;

Labels werden unter dem Thema Anweisungen mit Sprungmarken erörtert.

Ellipsen

Die Ellipse (...) besteht aus drei aufeinanderfolgenden Punkten ohne Whitespace. Ellipsen kennzeichnen in den formalen Argumentlisten der Funktionsprototypen eine variable Anzahl von Argumenten oder Argumente mit nicht festgelegten Typen. Die Deklaration

void func(int n, char ch,...);

besagt, daß Aufrufe von func mindestens zwei Argumente haben müssen, ein int und ein char. Daneben können beliebig viele Argumente vorhanden sein.

In C++ ist das Komma vor den Ellipsen optional.

Sternchen (Zeigerdeklaration)

Das Sternchen (*) in einer Variablendeklaration zeigt an, daß ein Zeiger auf einen Typ generiert wird:

char *char_ptr;  /* es wird ein Zeiger auf char deklariert */

Zeiger mit mehrfachen Indirektionsebenen werden durch mehrere Sternchen gekennzeichnet:

int **int_ptr;         /* ein Zeiger auf einen integer Array */

double ***double_ptr;  /* ein Zeiger auf ein double- Matrix */

Ein Sternchen kann auch als Operator, der einen Zeiger dereferenziert, oder als Multiplikationsoperator dienen.

i = *int_ptr;

a = b * 3.14;

Gleichheitszeichen (Initialisierer)

Das Gleichheitszeichen (=) trennt Variablendeklarationen von Initialisierungslisten:

char array[5] = { 1, 2, 3, 4, 5 };

int  x = 5;

In C++ können – mit wenigen Einschränkungen – alle Deklarationstypen an jeder beliebigen Stelle im Programm stehen. In einer C-Funktion darf vor den Variablendeklarationen kein Quelltext stehen.

In der Argumentliste einer C++ Funktion kennzeichnet das Gleichheitszeichen den Vorgabewert für einen Parameter:

int f(int i = 0) { ... }  /* Parameter i hat den Standardwert Null */

Das Gleichheitszeichen wird auch als Zuweisungsoperator in Ausdrücken verwendet:

int a, b, c;
a = b + c;

float *ptr = (float *) malloc(sizeof(float) * 100);

Nummernzeichen (Präprozessor-Anweisung)

Das Nummernzeichen (#) fungiert als Präprozessor-Anweisung, wenn es als erstes Nicht-Whitespace-Zeichen in einer Zeile steht. Es gibt eine Compileraktion an, die nicht notwendigerweise mit der Codegenerierung verknüpft ist. Weitere Informationen über Anweisungen für den Präprozessor finden Sie unter Präprozessor-Anweisungen.

Die Zeichen # und ## (doppeltes Nummernzeichen) werden auch als Operatoren eingesetzt, die den Präprozessor anweisen, Symbole zu ersetzen bzw. zu verschmelzen.


Sprachstruktur

Siehe auch

Unter diesem Thema wird die formale Definition (Syntax) der C++Builder Sprache und ihrer Implementation in C++Builder. Hier werden die Regeln bestimmt, nach denen die Symbole zur Bildung von Ausdrücken, Anweisungen und anderen bedeutungstragenden Einheiten kombiniert werden können.


Deklarationen

Siehe auch

Im folgenden Abschnitt finden Sie eine kurze Zusammenfassung der Themen, die mit Deklarationen zu tun haben: Objekte, Typen, Speicherklassen, Gültigkeitsbereiche, Sichtbarkeit, Lebensdauer und Bindung. Mit diesen Regeln sollten Sie prinzipiell vertraut sein, bevor Sie sich mit der Deklarationssyntax beschäftigen. Gültigkeitsbereich, Sichtbarkeit, Lebensdauer und Bindung legen diejenigen Programmteile fest, von denen aus ein zulässiger Zugriff auf das mit dem Bezeichner verknüpfte Objekt möglich ist.


Objekte

Siehe auch

Ein Objekt ist ein identifizierbarer Speicherbereich, in dem ein fester oder variabler Wert (oder eine Menge von Werten) steht. (Die Bezeichnung Objekt an dieser Stelle darf nicht mit dem allgemeineren OOP-Begriff "Objekt" verwechselt werden.) Jeder Wert hat einen assoziierten Namen und einen Typ (den sogenannten Datentyp). Der Zugriff auf das Objekt erfolgt über seinen Namen. Dieser Name kann ein einfacher Bezeichner oder ein komplexer Ausdruck sein, der eindeutig auf das Objekt verweist. Der Typ wird verwendet, um

die zu Beginn erforderliche, korrekte Speicherreservierung zu bestimmen.

bei späteren Zugriffen die in den Objekten gespeicherten Bitmuster zu interpretieren.

bei vielen Typ-Überprüfung ungültige Zuweisungen zu erkennen.

C++Builder unterstützt sowohl vordefinierte als auch benutzerdefinierte Typen. Dazu gehören ganze Zahlen mit und ohne Vorzeichen in verschiedenen Größen, Gleitkommazahlen mit unterschiedlicher Genauigkeit, Strukturen, Varianten, Arrays und Klassen. Zusätzlich können Zeiger auf fast alle Objekte definiert und im Speicher verwendet werden.

Die Standardbibliotheken von C++Builder sowie Ihre eigenen Programm- und Header-Dateien müssen eindeutige Bezeichner (oder von ihnen abgeleitete Ausdrücke) und Typen enthalten. Nur dann ist der konsequente Zugriff, die Interpretation und (gegebenenfalls) die Änderung des einem aktiven Objekt Ihres Programmes entsprechenden Bitmusters im Speicher sichergestellt.

Objekte und Deklarationen

Deklarationen stellen die notwendige Verbindung zwischen Bezeichnern und Objekten her. Jede Deklaration verknüpft einen Bezeichner mit einem Datentyp. Darüber hinaus bestimmen die meisten Deklarationen, die sogenannten Definitionsdeklarationen, auch die Generierung des Objektes (wo und wann), d.h., sie übernehmen die Zuweisung des Speicherplatzes und bei Bedarf seine Initialisierung. Andere Deklarationstypen, sogenannte Referenzdeklarationen, geben dem Compiler lediglich ihre Bezeichner und Typen bekannt. Für einen Bezeichner kann es viele Referenzdeklarationen geben, besonders in einem Programm mit vielen Dateien. Es darf aber nur eine Definitionsdeklaration für diesen Bezeichner geben.

Generell gilt: Ein Bezeichner kann in einem Programm nicht vor seiner Deklaration im Quelltext verwendet werden. Zulässige Ausnahmen von dieser Regel, sogenannte forward-Referenzen, sind Labels, Aufrufe von nicht deklarierten Funktionen und die "Tags" von Klassen, Strukturen und Varianten.

L-Werte

Ein L-Wert (lvalue) ist ein Objektzeiger: ein Ausdruck, der ein Objekt kennzeichnet. Ein Beispiel für einen L-Wert-Ausdruck ist *P, wobei P ein Ausdruck für einen Zeiger ungleich Null ist. Ein modifizierbarer L-Wert ist ein Bezeichner oder ein Ausdruck, der zu einem Objekt gehört, auf das zugegriffen werden kann und das im Speicher geändert werden darf. Ein const-Zeiger auf eine Konstante ist zum Beispiel kein modifizierbarer L-Wert. Ein Zeiger auf eine Konstante kann geändert werden, nicht aber deren dereferenzierter Wert.

Historisch steht L für "links", das heißt, daß ein L-Wert auf der linken Seite einer Zuweisungsanweisung (auf der empfangenden Seite) stehen darf. Jetzt dürfen nur noch modifizierbare L-Werte auf der linken Seite einer Zuweisungsanweisung stehen. Sind a und b beispielsweise nichtkonstante Integer-Bezeichner mit einem entsprechend zugewiesenen Speicherplatz, dann handelt es sich bei beiden um modifizierbare L-Werte. Somit sind Zuweisungen wie etwa a=1 und b=a+b zulässig.

R-Werte

Der Ausdruck a + b ist kein L-Wert. a + b = a ist nicht zulässig, weil der Ausdruck auf der linken Seite kein Objekt bezeichnet. Derartige Ausdrücke werden oft als R-Werte bezeichnet (als Abkürzung für "rechte" Werte).


Typen und Speicherklassen
Speicherklassen und -typen

Siehe auch

Mit Objekten verknüpfte Bezeichner müssen mindestens zwei Attribute haben: Speicherklasse und Typ (manchmal als Datentyp bezeichnet). Der C++Builder Compiler holt sich diese Attribute aus den impliziten und expliziten Deklarationen im Quelltext.

Die Speicherklasse legt die Position (Datensegment, Register, Heap oder Stack) und die Dauer oder die "Lebensdauer" des Objektes fest (das kann die gesamte Laufzeit eines Programmes oder die Ausführungszeit eines Blockes sein). Die Speicherklasse kann entweder durch die Syntax der Deklaration oder durch deren Position im Quelltext angegeben werden bzw. auf beide Arten.

Der Typ bestimmt, wieviel Speicherplatz einem Objekt zugeordnet wird und wie das Programm das im Speicherbereich des Objektes vorhandene Bitmuster interpretieren soll. Ein Datentyp kann als die Menge der Werte angesehen werden (häufig von der Implementierung abhängig), die Bezeichner dieses Typs annehmen können, in Verbindung mit der Menge von Operationen, die mit diesen Werten durchgeführt werden dürfen. Mit dem speziellen Operator sizeof (der während der Kompilierung ausgewertet wird) können Sie für jeden benutzerdefinierten und standardmäßigen Typ die Größe in Byte ermitteln.


Gültigkeitsbereich

Siehe auch

Der Gültigkeitsbereich eines Bezeichners ist der Programmteil, in dem über den Bezeichner auf das Objekt zugegriffen werden kann. Es gibt fünf Kategorien für den Gültigkeitsbereich: Block (oder lokal), Funktion, Funktionsprototyp, Datei und Klasse (nur in C++). In welche Kategorie der Gültigkeitsbereich fällt, hängt davon ab, wie und wo Bezeichner deklariert werden.

Block. Der Block- oder lokale Gültigkeitsbereich beginnt beim Deklarationspunkt und hört am Ende des Blockes auf, der die Deklaration enthält (ein derartiger Block wird einschließender Block genannt). Hierzu gehören auch Parameterdeklarationen innerhalb einer Funktionsdefinition.

Funktion. Labels für Anweisungen sind die einzigen Bezeichner, die eine Funktion als Gültigkeitsbereich haben. Labelnamen können mit goto-Anweisungen an beliebiger Stelle in der Funktion, in der das Label deklariert wird, verwendet werden. Die Deklaration von Labeln erfolgt implizit durch die Angabe von label_name:, gefolgt von einer Anweisung. Derselbe Labelname darf in einer Funktion nur einmal vorkommen.

Funktionsprototyp. Bezeichner, die in der Liste der Parameterdeklarationen eines Funktionsprototyps (die nicht Teil einer Funktionsdefinition ist) deklariert werden, haben als Gültigkeitsbereich den Funktionsprototyp. Dieser Gültigkeitsbereich endet mit dem Ende des Funktionsprototyps.

Datei. Bezeichner mit dem Gültigkeitsbereich Datei, sogenannte globale Bezeichner, werden außerhalb aller Klassen und Blöcke deklariert. Ihr Gültigkeitsbereich erstreckt sich vom Deklarationspunkt bis zum Ende der Quelldatei.

Klasse (C++). Vereinfacht gesagt ist eine Klasse eine benannte Ansammlung von Elementen, einschließlich der Datenstrukturen und Funktionen, die auf diese zugreifen. Der Gültigkeitsbereich Klasse wird auf die Namen von Elementen einer bestimmten Klasse angewendet. Für Klassen und ihre Objekte gelten viele spezielle Zugriffs- und Gültigkeitsbereichsregeln (siehe auch Klassen).

Bedingung (C++). Deklarationen mit Bedingungen werden unterstützt. Variablen können innerhalb eines Ausdrucks mit if-, while- oder switch-Anweisungen deklariert werden. Der Gültigkeitsbereich der Variable ist mit dem der Anweisung identisch. Handelt es sich um eine if-Anweisung, befindet sich die Variable ebenfalls im Gültigkeitsbereich des else-Blocks.

Namensbereiche

Ein Namensbereich ist der Bereich, in dem derselbe Bezeichner nur einmal vorkommen darf. Es gibt in C vier verschiedene Bezeichnerklassen:

goto-Labelnamen. Diese Bezeichner müssen innerhalb der Funktion, in der sie deklariert werden, eindeutig sein.

Struktur-, Varianten- und Aufzählungs-"Tags". Diese Bezeichner müssen innerhalb des Blockes, in dem sie definiert werden, eindeutig sein. "Tags", die außerhalb einer Funktion deklariert werden, müssen innerhalb aller extern definierten "Tags" eindeutig sein.

Namen von Struktur- und Variantenelementen. Diese Bezeichner müssen innerhalb der Struktur oder Variante, in der sie definiert werden, eindeutig sein. Für Elemente mit gleichem Namen in verschiedenen Strukturen gibt es keine Restriktionen bezüglich Typ und Offset.

Variablen, typedefs, Funktionen und Elemente von Aufzählungen. Diese Bezeichner müssen innerhalb des Gültigkeitsbereichs, in dem sie definiert werden, eindeutig sein. Extern deklarierte Bezeichner müssen unter den extern deklarierten Variablen eindeutig sein.

Hinweis:	Strukturen, Klassen und Aufzählungen liegen in C++ im gleichen Namensbereich.


Sichtbarkeit

Siehe auch

Unter der Sichtbarkeit eines Bezeichners versteht man den Bereich des Quelltexts, von dem aus der Zugriff auf das mit dem Bezeichner verknüpfte Objekt zulässig ist.

Gültigkeitsbereich und Sichtbarkeit fallen normalerweise zusammen. Es kann jedoch Konstellationen geben, bei denen ein Objekt vorübergehend durch das Auftreten eines doppelten Bezeichners verborgen wird. Das Objekt existiert weiterhin, aber es kann kein Zugriff mit dem ursprünglichen Bezeichner erfolgen. Dieser ist erst wieder möglich, wenn der Gültigkeitsbereich des doppelten Bezeichners endet.

Hinweis:	Die Sichtbarkeit kann nicht größer sein als der Gültigkeitsbereich, während dies umgekehrt möglich ist.

    .

    .

    .

{

   int i; char ch;  // per Vorgabe automatisch

   i = 3;           // int i und char ch im Gültigkeitsbereich und sichtbar

    .

    .

    .

   {

      double i;

      i = 3.0e3;    // double i im Gültigkeitsbereich und sichtbar

                    // int i=3 im Gültigkeitsbereich aber verborgen

      ch = 'A';     // char ch im Gültigkeitsbereich und sichtbar

   }

                    // double i außerhalb des Gültigkeitsbereichs

   i += 1;          // int i sichtbar und = 4

    .

    .

    .

// char ch weiterhin im Gültigkeitsbereich und sichtbar = 'A'

}

    .

    .

    .

// int i und char ch außerhalb des Gültigkeitsbereichs

In C++ gelten für verborgene Klassen und Klassenelementnamen besondere Regeln: Durch spezielle C++-Operatoren kann unter bestimmten Umständen auch auf verborgene Bezeichner zugegriffen werden.


Lebensdauer

Siehe auch

Die Begriff Lebensdauer ist eng mit der Speicherklasse verwandt und definiert den Zeitraum, in dem deklarierten Bezeichnern im Speicher reale, physische Objekte zugewiesen sind. Es wird zwischen Kompilierungszeit- und Laufzeitobjekten unterschieden. Beispielsweise wird Variablen – im Gegensatz zu Objekten des Typs typedef und anderen Typen – während der Laufzeit realer Speicherplatz zugewiesen. Es gibt drei Arten von Lebensdauer: statisch, lokal und dynamisch.

Statisch

Objekten mit statischer Lebensdauer wird Speicherplatz zugewiesen, sobald die Ausführung beginnt. Die Speicherplatzzuweisung bleibt bestehen, bis das Programm endet. Sie werden normalerweise in festen Datensegmenten gespeichert, denen entsprechend des benutzten Speichermodells Platz zugewiesen wurde. Alle Funktionen – unabhängig davon, wo sie definiert werden – sind Objekte mit statischer Lebensdauer. Variablen mit dem Gültigkeitsbereich Datei besitzen ebenfalls statische Lebensdauer. Anderen Variablen kann durch die Zuweisung der Speicherklassen-Bezeichner static oder extern statische Lebensdauer zugeordnet werden.

Objekte mit statischer Lebensdauer werden auf Null gesetzt, wenn kein expliziter Initialisierer vorhanden ist oder  von einem Konstruktor (in C++).

Statische Lebensdauer darf nicht mit dem Gültigkeitsbereich Datei oder Global verwechselt werden. Ein Objekt mit statischer Lebensdauer kann gleichzeitig einen lokalen Gültigkeitsbereich haben.

Lokal

Objekte mit lokaler Lebensdauer, auch als automatische Objekte bezeichnet, besitzen eine unsicherere Existenz. Sie werden auf dem Stack (oder in einem Register) angelegt, sobald das Programm in einen einschließenden Block oder in eine Funktion eintritt. Diese Objekte verlieren ihren Speicherplatz wieder, wenn das Programm die Funktion oder den Block verläßt. Objekte mit lokaler Lebensdauer müssen explizit initialisiert werden, da ihr Inhalt ansonsten nicht vorhersehbar ist. und sie müssen einen lokalen Gültigkeitsbereich bzw. einen Funktions-Gültigkeitsbereich haben. Der Speicherklassen-Bezeichner auto kann bei der Deklaration von Variablen mit lokaler Lebensdauer verwendet werden. Seine Verwendung ist aber normalerweise überflüssig, da auto für Variablen, die innerhalb von Blöcken deklariert sind, voreingestellt ist. Ein Objekt mit lokaler Lebensdauer hat einen lokalen Gültigkeitsbereich, weil es außerhalb des einschließenden Blocks nicht existiert. Die Umkehrung gilt jedoch nicht: Ein Objekt mit lokalem Gültigkeitsbereich kann eine statische Lebensdauer haben.

Bei der Deklaration von Variablen (zum Beispiel int, char, float) impliziert der Speicherklassen-Bezeichner register gleichzeitig auto; der Compiler wird jedoch aufgefordert, dem Objekt – wenn möglich – ein Register zuzuweisen. C++Builder kann so eingestellt werden, daß einer lokalen Integer- oder Zeigervariablen immer ein Register zugewiesen wird, wenn dies möglich ist. Ist kein Register frei, wird die Variable ohne Warnung oder Fehlermeldung als lokales Objekt der Speicherklasse auto zugeordnet.

Hinweis:	Der C++Builder Compiler kann die Anforderung einer Registerzuweisung ignorieren. Registerzuweisung beruht auf der Analyse des Compilers über die Verwendung einer Variable.

Dynamisch

Objekte mit dynamischer Lebensdauer werden während eines Programms mit besonderen Funktionsaufrufen angelegt und wieder freigegeben. Die Speicherzuweisung erfolgt entweder über Standardbibliotheksfunktionen wie malloc oder mit dem C++ Operator new. Der zugewiesene Speicherplatz liegt in einem bestimmten Speicherbereich, dem sogenannten Heap. Mit free oder delete wird der Speicherplatz wieder freigegeben.


static

Beispiel	Schlüsselwörter

Syntax

static <data definition> ;

static <function name> <function definition> ;

Description

Verwenden Sie den Speicherklassenbezeichner static mit einer lokalen Variablen, um den letzten Wert zwischen aufeinanderfolgenden Aufrufen dieser Funktion zu erhalten. Eine static-Variable verhält sich wie eine lokale Variable, hat jedoch die Lebensdauer einer externen Variablen. In einer Klasse können Daten- und Elementfunktionen als static deklariert werden. Es existiert nur eine Kopie der static-Daten für alle Objekte der Klasse.

Eine static-Elementfunktion einer globalen Klasse hat eine externe Bindung. Ein Element einer lokalen Klasse hat keine Bindung. Eine static-Elementfunktion ist nur mit der Klasse verbunden, in welcher sie deklariert wurde. Aus diesem Grunde können solche Elementfunktionen nicht virtual sein.

Statische Elementfunktionen können nur andere static-Elementfunktionen aufrufen und haben nur Zugriff auf static-Daten. Solche Elementfunktionen haben keinen this-Zeiger.


Übersetzungseinheiten

Siehe auch

Der Ausdruck Übersetzungseinheit bezieht sich auf eine Quelltextdatei und ihre Include-Dateien. Nicht dazu zählen Quelltextzeilen, die durch Präprozessor-Direktiven ausgeschlossen werden. Syntaktisch wird eine Übersetzungseinheit als eine Abfolge externer Deklarationen definiert:

translation-unit:

external-declaration

translation-unit external-declaration

external-declaration

function-definition

declaration

Der Begriff extern hat in C verschiedene Bedeutungen. Im vorliegenden Kontext wird es für Deklarationen verwendet, die außerhalb einer Funktion definiert werden und die deshalb den Gültigkeitsbereich Datei haben. (Beachten Sie, daß extern in Zusammenhang mit dem Linken eine besondere Bedeutung hat; Näheres dazu erfahren Sie unter Bindung.) Jede Deklaration, die Speicher für ein Objekt oder eine Funktion bereitstellt, wird als Definition (oder als definierende Deklaration) bezeichnet. Weitere Informationen hierzu erhalten Sie unter External-Deklarationen und -Definitionen.


Bindung

Siehe auch

Ein ausführbares Programm entsteht normalerweise durch die Kompilierung von verschiedenen, voneinander unabhängigen Übersetzungseinheiten und anschließendem Linken der resultierenden Objektdateien mit bereits vorhandenen Bibliotheken. Probleme können entstehen, wenn derselbe Bezeichner in verschiedenen Gültigkeitsbereichen (etwa in unterschiedlichen Dateien) oder mehr als einmal im gleichen Gültigkeitsbereich deklariert ist. Linken ist der Vorgang, der die korrekte Verknüpfung zwischen jedem Auftreten eines Bezeichners und einem Objekt oder einer bestimmten Funktion herstellt. Jeder Bezeichner hat eines der drei Bindungsattribute, die im engen Zusammenhang mit dem Gültigkeitsbereich stehen: externe, interne oder keine Bindung. Diese Attribute werden von der Position und dem Format Ihrer Deklarationen, sowie dem expliziten (oder durch Voreinstellung impliziten) Einsatz der Speicherklassen-Bezeichner static oder extern bestimmt.

Jedes Auftreten eines bestimmten Bezeichners mit externer Bindung repräsentiert in allen Dateien und Bibliotheken eines Programms dasselbe Objekt oder dieselbe Funktion. Jedes Auftreten eines Bezeichners mit interner Bindung repräsentiert dasselbe Objekt oder dieselbe Funktion nur in einer Datei. Bezeichner ohne Bindung repräsentieren eindeutige Einheiten.

Regeln für das externe und interne Binden

Alle Objekt- und Dateibezeichner, die den Gültigkeitsbereich Datei besitzen, verfügen über interne Bindung, wenn ihre Deklaration den Speicherklassen-Bezeichner static enthält.

Kommt derselbe Bezeichner innerhalb derselben Datei mit interner und externer Bindung vor, so erhält er in C interne Bindung und in C++ externe Bindung.

Enthält die Deklaration eines Objekt- oder Funktionsbezeichners den Speicherklassen-Bezeichner extern, hat der Bezeichner dieselbe Bindung wie jede sichtbare Deklaration des Bezeichners mit dem Gültigkeitsbereich Datei. Ist keine derartige Deklaration vorhanden, hat der Bezeichner externe Bindung.

Wird eine Funktion ohne Speicherklassen-Bezeichner deklariert, wird ihre Bindung so festgelegt, als ob der Bezeichner extern verwendet worden wäre.

Wenn ein Objektbezeichner mit dem Gültigkeitsbereich Datei ohne einen Speicherklassen-Bezeichner deklariert wird, hat er externe Bindung.

Folgende Bezeichner haben das Attribut Keine Bindung:

Jeder Bezeichner, der nicht als Objekt- oder Funktionsbezeichner deklariert wird (zum Beispiel ein typedef-Bezeichner)

Funktionsparameter

Bezeichner mit dem Gültigkeitsbereich Block für Objekte, die nicht zu der Speicherklasse extern gehören

Ergänzen von Namen

Wenn ein C++ Modul kompiliert wird, erzeugt der Compiler Funktionsnamen, die die Typen der Funktionsargumente in verschlüsselter Form enthalten. Dieser Vorgang wird als Namensergänzung (im Englischen "name mangling") bezeichnet. Dadurch wird die Verwendung überladener Funktionen möglich. Außerdem ist der Linker besser in der Lage, Fehler in Aufrufen von Funktionen in anderen Modulen herauszufinden. Es gibt jedoch Situationen, in denen die Namensergänzung nicht wünschenswert ist. Wenn ein C++ Modul mit einem Modul gelinkt werden soll, das keine ergänzten Namen enthält, muß dem C++ Compiler mitgeteilt werden, daß er die Funktionsnamen aus den anderen Modulen nicht ergänzen soll. Eine derartige Situation ist dann gegeben, wenn Bibliotheken oder OBJ-Dateien gelinkt werden, die mit einem C-Compiler kompiliert wurden.

Wenn der C++ Compiler den Namen einer Funktion nicht ergänzen soll, deklarieren Sie die Funktion als extern "C" wie in folgendem Beispiel:

extern "C" void Cfunc( int );

Diese Deklaration veranlaßt den Compiler dazu, die Referenzen auf die Funktion Cfunc nicht zu ergänzen.

Sie können die Deklaration extern "C" auch auf einen Namensblock anwenden:

extern "C" {

   void Cfunc1( int );

   void Cfunc2( int );

   void Cfunc3( int );

};

Genau wie bei der Deklaration für eine einzelne Funktion wird auch hier dem Compiler mitgeteilt, daß Referenzen auf die Funktionen Cfunc1, Cfunc2 und Cfunc3 nicht ergänzt werden sollen. Diese Art der Blockdeklaration können Sie auch dann verwenden, wenn der Funktionsnamen-Block in einer Header-Datei steht:

extern "C" {

   #include "locallib.h"

};


Einführung in die Deklarationssyntax

Siehe auch

Alle sechs miteinander verknüpften Attribute (Speicherklasse, Typ, Gültigkeitsbereich, Sichtbarkeit, Dauer und Bindung) werden auf verschiedene Art und Weise über Deklarationen festgelegt.

Es gibt zwei Gruppen von Deklarationen: definierende Deklarationen (oder einfacher: Definitionen) und Referenzdeklarationen (manchmal werden sie auch nicht-definierende Deklarationen genannt). Eine definierende Deklaration beinhaltet – wie der Name bereits sagt – sowohl eine Deklaration als auch eine Definition; Referenzdeklarationen (oder nicht-definierende Deklarationen) benötigen eine Definition an einer anderen Stelle im Programm. Referenzdeklarationen führen einen oder mehrere Bezeichnernamen in das Programm ein. Eine Definition bewirkt eine Speicherplatzzuweisung zu einem Objekt und verbindet einen Bezeichner mit diesem Objekt.


Tentative Definitionen

Siehe auch

Der ANSI-C-Standard unterstützt die Idee der tentative Definition. Jede externe Datendeklaration, die keinen Speicherklassen-Spezifizierer und keinen Initialisierer hat, wird als tentative Definition betrachtet. Wenn der deklarierte Bezeichner später definiert wird, dann wird die tentative Definition behandelt, als wäre der Speicherklassen-Bezeichner extern angegeben worden. Das heißt: Die tentative Definition wird zu einer einfachen Referenzdeklaration.

Wird das Ende der Übersetzungseinheit erreicht und keine Definition mit einem Initialisierer für den Bezeichner gefunden, dann wird die tentative Definition zu einer vollen Definition. Der Speicher für das Objekt wird reserviert, aber nicht initialisiert (d.h., mit Null gefüllt). Betrachten Sie folgendes Beispiel:

int x;

int x;           /*zulässig, eine Kopie von x ist reserviert */

int y;

int y = 4;       /* zulässig, y wird mit 4 initialisiert */

int z = 5;

int z = 6;       /* nicht zulässig, z wird zum zweiten Mal initialisiert */

Im Gegensatz zu ANSI C verfügt C++ nicht über tentative Deklaration; eine externe Datendeklaration ohne Speicherklassen-Bezeichner ist immer eine Definition.


Mögliche Deklarationen

Siehe auch

Folgende Objekte können deklariert werden:

Variablen

Funktionen

Klassen und Elemente von Klassen (C++)

Typen

Strukturen-, Varianten- und Aufzählungs-Tags

Elemente von Strukturen

Elemente von Varianten

Arrays anderer Typen

Aufzählungskonstanten

Anweisungs-Sprungmarken (Labels)

Präprozessor-Makros

Die vollständige Syntax für die Deklarationen finden Sie in den Tabellen 2.1 bis 2.3. Die rekursive Natur der Deklarationssyntax erlaubt sehr komplexe Deklaratoren. Sie sollten deshalb versuchen, durch Verwendung von typedef die Lesbarkeit zu verbessern.

Beachten Sie in der Tabelle C++Builder Deklarationssyntax die Einschränkungen bezüglich Anzahl und Reihenfolge von Modifizierern und Qualifizierern. Die aufgeführten Modifizierer sind die einzige Erweiterung der von ANSI C oder C++ vorgegebenen Deklarationssyntax. Nähere Informationen hierzu erhalten Sie unter Variablen-Modifizierer, und Funktions-Modifizierer.

C++Builder Deklarationssyntax

Deklaration:	Erweiterter Typ-Bezeichner:

<Mehrere Deklarations-Spezifiz.>  <Deklarator-Liste>;	Klassenschlüssel  Bezeichner

asm-Deklaration	Klassenschlüssel  Klassenname

Funktionsdeklaration	enum Aufzählungsname

Bindungs-Spezifikation	Klassenschlüssel: (C++ spezifisch)

Deklarations-Spezifizierer:	class

Speicherklassen-Spezifizierer	struct

Typ-Spezifizierer	union

Funktions-Spezifizierer	Aufzählungsspezifizierer:

friend (C++ spezifisch)	enum <Bezeichner> { < Aufzählungsliste > }

typedef	Aufzählungsliste:

Mehrere Deklarations-Spezifizierer:	Aufzähler

<Mehrere Deklarat.-Spezifiz.> Deklarations-Spezifiz.	Aufzählungsliste, Aufzähler

Speicherklassen-Bezeichner:	Aufzähler:

auto	Bezeichner

register	Bezeichner = Konstantenausdruck

static	Konstantenausdruck:

extern	Bedingter Ausdruck

Funktions-Bezeichner: (C++ spezifisch)	Bindungs-Spezifizierung: (C++ spezifisch)

inline	extern string { <Deklarationsliste> }

virtual	extern string Deklaration

Einfacher Typname:	Typ-Bezeichner:

Klassenname	Einfacher Typname

typedef-Name	Klassen-Bezeichner

boolean

char	Aufzählungsbezeichner

short	Erweiterter Typ-Bezeichner

int	const

int8

int16

int32

int64

long	volatile

signed	Deklarationsliste:

unsigned	Deklaration

float	Deklarationsliste ; Deklaration

double

void

Deklarator-Liste:	Typname:

Initialisierungs-Deklarator	Typ-Bezeichner <Abstrakt-Deklarator>

Deklarator-Liste , Initialisierungs-Deklarator	Abstrakt-Deklarator:

Initialisierungs-Deklarator:	Zeiger-Operator < Abstrakt-Deklarator >

Deklarator <Initialisierer>	< Abstrakt-Deklarator > ( Argument-Deklarationsliste )

Deklarator:	< cv-Qualifizierer-Liste >

dname	< Abstrakt-Deklarator > [ < Konstantenausdruck > ]

Modifizierer-Liste	(Abstrakt-Deklarator )

Zeiger-Operator Deklarator	Argument-Deklarationsliste:

Deklarator ( Parameter-Deklarationsliste ) 	<Arg-Deklarationsliste>

<cv-Qualifizierer-Liste >	Arg-Deklarationsliste , ...

( <cv-Qualifizierer-Liste > nur bei C++.)	<Arg-Deklarationsliste> ... (C++ spezifisch)

Deklarator [ <Konstantenausdruck > ]	Arg-Deklarationsliste:

( Deklarator )	Argumentdeklaration

Modifizierer-Liste:	Arg-Deklarationsliste , Argumentdeklaration

Modifizierer	Argumentdeklaration:

Modifizierer-Liste  Modifizierer	Mehrere-Deklarations-Spezifizierer Deklarator

Modifizierer:	Mehrere-Deklarations-Spezifizierer Deklarator = Ausdruck

_ _cdecl	(C++ spezifisch)

_ _pascal	Mehrere-Deklarations-Spezifizierer <Abstrakt-Deklarator>

_ _stdcall	Mehrere-Deklarations-Spezifizierer < Abstrakt-Deklarator > = Ausdruck

_ _fastcall	(C++ spezifisch)

Funktionsdefinition:

Funktionsrumpf:

Zeiger-Operator:	Blockanweisung

* < cv-Qualifizierer-Liste >	Initialisierer:

& < cv-Qualifizierer-Liste > (C++ spezifisch)	= Ausdruck

Klassenname :: * < cv-Qualifizierer-Liste > 	= { Initialisierer-Liste }

(C++ spezifisch)	( Ausdrucksliste ) (C++ spezifisch)

cv-Qualifizierer-Liste:	Initialisierer-Liste:

cv-Qualifizierer < cv-Qualifizierer-Liste >	Ausdruck

cv-Qualifizierer	Initialisierer-Liste , Ausdruck

const	{ Initialisierer-Liste <,> }

volatile

dname:

Name

Klassenname (C++ spezifisch)

~ Klassenname (C++ spezifisch)

typedef-Name


Externe Deklarationen und Definitionen

Siehe auch

Die Speicherklassen-Bezeichner auto und register dürfen nicht in einer external -Deklaration verwendet werden. Für jeden Bezeichner in einer Übersetzungseinheit, der mit interner Bindung deklariert ist, darf nur eine externe Definition angegeben werden.

Eine externe Definition ist eine externe Deklaration, die ein Objekt oder eine Funktion zusätzlich definiert, also auch Speicherplatz reserviert. Wird ein Bezeichner mit externer Bindung in einem Ausdruck verwendet (außer als Teil des Operanden von sizeof), muß es im Programm an beliebiger Stelle genau eine externe Definition für diesen Bezeichner geben.

C++Builder erlaubt, daß externe Namen (wie die von Arrays, Strukturen und Varianten) später umdeklariert werden. Auf diese Weise können früheren Deklarationen Informationen hinzugefügt werden. Hierzu ein Beispiel:

int a[];              // keine Größenangabe

struct mystruct;      // nur ein "Tag", keine Element-Deklarationen

  .

  .

  .

int a[3] = {1, 2, 3}; // Größe angeben und initialisieren

struct mystruct {

   int i, j;

};                    // Element-Deklarationen hinzufügen

Die Tabelle C++Builder Syntax für die Deklaration von Klassen (nur C++) enthält die Deklarationssyntax für Klassen, Beispiele hierzu finden Sie unter dem Thema Klassen. Das Thema Referenzen enthält detaillierte Informationen zu C++ Referenz-Typen (nahe verwandt mit Zeiger-Typen). Eine Erläuterung von Klassen-Templates finden Sie unter Verwenden von Templates.

C++Builder Syntax für die Deklaration von Klassen (nur C++)

Klassen-Bezeichner:	Basis-Bezeichner:

Klassenkopf { <Element-Liste> }	 : Basisliste

Klassenkopf:	Basisliste:

Klassenschlüssel <Bezeichner> <Basis-Bezeichner>	Basis-Bezeichner

Klassenschlüssel Klassenname <Basis-Bezeichner>	Basisliste , Basis-Bezeichner

Element-Liste:	Basis-Bezeichner:

Element-Deklaration <Element-Liste>	Klassenname

Zugriffs->Bezeichner : <Element-Liste>	virtual <Zugriffs-Bezeichner> Klassen-Name

Element-Deklaration:	Zugriffs-Bezeichner<virtual> Klassen-Name

<Mehrere-Deklar.-Bez.> <Element-Deklaratorliste> ;	Zugriffs-Bezeichner:

Funktionsdefinition <;>	private

Qualifizierter Name ;	protected

Element-Deklaratorliste:	public

Element-Deklarator	Konvertierungs-Funktions-Name:

Element-Deklaratorliste, Element-Deklarator	operator Konvertierungs-Typname

Element-Deklarator:	Konvertierungs-Typname:

Deklarator <Abstrakt-Bezeichner>	Typ-Bezeichner <Zeiger-Operator >

<Bezeichner> : Konstantenausdruck	Konstruktor-Initialisierer:

Abstrakt-Bezeichner:	: Element-Initialisierer-Liste

= 0

Element-Initialisierer-Liste:	Operatorname: eines der folgenden Zeichen

Element-Initialisierer	new  delete  sizeof  typeid

Element-Initialisierer , Element-Initialisierer-Liste	+	-	*	 /	%	^

Element-Initialisierer:	&	|	~	!	=	<>

Klassenname  ( <Argumentliste> )	+=	-=	=*	/=	%=	^=

Bezeichner  ( <Argumentliste> )	&=	|=	<<	>>	>>=	<<=

Operator-Funktionsname:	==	!=	<=	>=	&&	||

operator Operatorname	++	__	,	->*	->	()

[ ]	.*


Typ-Spezifizierer
Typ-Bezeichner

Siehe auch

Der Typ legt fest, wieviel Speicher für ein Objekt reserviert wird, und wie das Programm die Bit-Parameter aus der Speicheralloziierung des Objekts interpretiert. Ein Datentyp ist der Satz von Werten (oft Implementationsabhängig) die Bezeichner, zusammen mit dem Satz an Operatoren die für diese Werte erlaubt sind, annehmen können

Ein Typ-Bezeichner mit einem oder mehreren Modifizierern legt den Typ des deklarierten Bezeichners fest:

int i;                    // deklariert i als ein Integer

unsigned char ch1, ch2;  // deklariert zwei vorzeichenlose Zeichen

Traditionell wird bei fehlendem Typ-Bezeichner der Typ signed int ( = int) als Voreinstellung angenommen. In C++ gibt es jedoch Situationen, in denen das Fehlen des Typ-Spezifizierers zu syntaktischen Mehrdeutigkeiten führen kann. Aus diesem Grund sollten in C++ immer alle int-Typ-Spezifizierer angegeben werden.

Die Typbezeichner-Schlüsselwörter in C++Builder sind:

char	float	signed	wchar_t

class	int	struct

double	long	union

enum	short	unsigned

Verwenden Sie die sizeof-Operatoren, um die Größe in Bytes eines vordefinierten oder benutzerdefinierten Typs zu ermitteln.


Typ-Kategorien

Siehe auch

Es folgen die vier grundlegenden Typkategorien und ihre Unterkategorien:

Aggregate

Array

struct

union

class (nur in C++)

Funktion

Skalar

Arithmetisch

Aufzählung

Zeiger

Referenz (nur in C++)

void (siehe Thema der Typ void)

Typen lassen sich auch in grundlegende und abgeleitete Typen einteilen. Grundlegende Typen sind: void, char, int, float und double sowie deren short-, long-, signed- und unsigned-Varianten. Die abgeleiteten Typen umfassen Zeiger und Referenzen auf andere Typen, Arrays anderer Typen, Funktions- und Klassentypen, Strukturen und Varianten.

Ein Klassenobjekt kann zum Beispiel eine Anzahl von Objekten mit unterschiedlichen Typen enthalten. Dazu gehören auch Funktionen zur Bearbeitung dieser Objekte und ein Mechanismus zur Steuerung des Zugriffs und der Vererbung von anderen Klassen.

Sie können aus fast jedem Typ 'type' (außer void) die folgenden abgeleiteten Typen deklarieren:

Deklarieren von Typen

Deklaration	Beschreibung

type t;	Ein Objekt vom Typ type

type array[10];	Zehn Objekte vom Typ type: array[0] - array[9]

type *ptr;	ptr ist ein Zeiger auf type

type &ref = t;	ref ist eine Referenz auf type (C++)

type func(void);	func gibt Wert vom Typ type zurück

void func1(type t);	func1 erwartet Parameter vom Typ type

struct st {type t1; type t2};	Struktur st enthält zwei Werte vom Typ type

Hinweis:	Die Schreibweisen type& var, type &var und type & var sind gleichwertig.


void

Beispiel	Schlüsselwörter

Syntax

void identifier

Beschreibung

void ist ein spezieller Typ, der das Fehlen eines Wertes anzeigt. Verwenden Sie das Schlüsselwort void als Rückgabewert einer Funktion, wenn die Funktion keinen Wert zurückgibt..

void hello(char *name)

{

  printf("Hello, %s.",name);

}

Die Angabe void in der Parameterliste einer Funktion bedeutet, daß die Funktion keine Parameter hat.

int init(void)

{

  return 1;

}

void-Zeiger

Generische Zeiger können ebenfalls als void deklariert werden, was bedeutet, daß sie auf jeden Typen zeigen können.

void-Zeiger können nicht ohne explizite Typumwandlung dereferenziert werden, weil der Compiler sonst die Größe des Objekts, auf das der Zeiger zeigt, nicht erkennen kann.


Grundlegende Typen

Siehe auch

Die grundlegenden Typ-Bezeichner werden aus den folgenden Schlüsselwörtern zusammengesetzt:

char	__int8	long

double	__int16	signed

float	__int32	short

int	__int64	unsigned

Mit diesen Schlüsselwörtern können Sie Integer- und Gleitkommazahlen zusammensetzen, die unter dem Oberbegriff arithmetische Typen zusammengefaßt werden. Die Modifizierer long, short, signed und unsigned können auf ganzzahlige Datentypen angewandt werden. In der Include-Datei limits.h sind die Wertebereiche für alle grundlegenden Typen definiert.

Integrale Typen

char, short, int und long sowie ihre vorzeichenlosen Varianten werden als ganzzahlige Datentypen bezeichnet. In der Tabelle Integer-Typen finden Sie die Spezifizierer für ganzzahlige Datentypen und ihre in der gleichen Zeile aufgeführten Synonyme.

Integrale Typen

char, signed char	Synonyme, wenn die Voreinstellung für char auf signed gesetzt ist.

unsigned char

char, unsigned char	Synonyme, wenn die Voreinstellung für char auf unsigned gesetzt ist.

signed char

int, signed int

unsigned, unsigned int

short, short int, signed short int

unsigned short, unsigned short int

long, long int, signed long int

unsigned long, unsigned long int

Hinweis:	Diese Synonyme gelten nicht in C++. Lesen Sie Die drei char-Typen.

Die Schlüsselwörter signed oder unsigned können nur zusammen mit char, short, int oder long verwendet werden. Wenn Sie die Schlüsselwörter signed oder unsigned alleine einsetzen, bedeuten sie signed int bzw. unsigned int.

Ist kein unsigned vorhanden, wird signed für integrale Typen verwendet. Eine Ausnahme besteht für char. In C++Builder kann für char sowohl signed als auch unsigned voreingestellt sein. (Falls Sie es nicht ändern, ist signed voreingestellt.) Ist die Voreinstellung unsigned, wird ch in der Deklaration char ch als unsigned deklariert. Sie müßten mit signed char ch die Voreinstellung überschreiben. Derselbe Fall liegt vor, wenn eine signed-Voreinstellung für char besteht. Sie müßten dann unsigned char ch explizit eingeben, um unsigned char zu deklarieren.

long oder short können nur mit int angegeben werden. Die Schlüsselwörter long und short alleine bedeuten long int und short int.

ANSI C schreibt die Größe oder interne Darstellung dieser Typen nicht vor. Es ist jedoch festgelegt, daß short, int und long nach dem Muster short <= int <= long dargestellt werden. Alle drei Typen können identisch sein. Dies ist zu beachten, wenn Sie portablen Programm-Quelltext schreiben möchten, der in anderen Umgebungen laufen soll.

In einem C++Builder 32-Bit-Programm sind die Typen int und long äquivalent; beide sind 32-Bit-Objekte. Die vorzeichenbehafteten Varianten werden im Zweier-Komplement-Format mit dem MSB (Most Significant Bit; also dem höchstwertigen Bit) als Vorzeichen gespeichert: 0 für positiv, 1 für negativ (dies erklärt die in 32-Bit Datentypen, -größen und Wertebereiche aufgeführten Wertebereiche). Bei den vorzeichenlosen Varianten werden alle Bits für einen Bereich von 0 – (2n – 1) verwendet, wobei n gleich 8, 16 oder 32 ist.

Gleitkommatypen

Darstellung und Menge der Werte für die Gleitkommatypen sind implementierungsabhängig; das heißt, sie können von jeder C-Implementierung frei definiert werden. C++Builder verwendet die IEEE-Gleitkomma-Formate. Weitere Informationen zu implementierungsabhängigen Themen finden Sie in ANSI Implementierungs-Spezifikationen.

float und double sind 32- bzw. 64-Bit-Gleitkommatypen. long zusammen mit double ergibt einen Gleitkommatyp mit einer Genauigkeit von 80-Bit (z.B. long double test_case).

Informationen über die Speicherreservierung von Gleitkommatypen finden Sie in 32-Bit Datentypen, -größen und Wertebereiche.

Arithmetische Standard-Umwandlungen

Wenn Sie einen arithmetischen Ausdruck wie a + b haben, wobei a und b zu verschiedenen arithmetischen Typen gehören, führt C++Builder vor der Auswertung des Ausdrucks bestimmte interne Konvertierungen durch. Diese Standardkonvertierungen umfassen auch die Umwandlung von "niederwertigen" Typen in "höherwertige", um Genauigkeit und Konsistenz zu gewährleisten.

C++Builder führt zur Umwandlung der Operanden eines arithmetischen Ausdrucks folgende Schritte durch:

1.	Alle kleinen Integertypen werden so konvertiert, wie Sie es in der Tabelle Verfahren für arithmetische Standard-Umwandlungen sehen. Danach sind die zwei mit einem Operator verbundenen Werte entweder int (einschließlich der long- und unsigned-Modifizierer) oder double, float oder long double.

2.	Wenn einer der beiden Operanden vom Typ long double ist, wird der andere nach long double konvertiert.

3.	Wenn einer der beiden Operanden vom Typ double ist, wird der andere nach double konvertiert.

4.	Wenn einer der beiden Operanden vom Typ float ist, wird der andere nach float konvertiert.

5.	Wenn einer der beiden Operanden vom Typ unsigned long ist, wird der andere nach unsigned long konvertiert.

6.	Wenn einer der beiden Operanden vom Typ long ist, wird der andere nach long konvertiert.

7.	Wenn einer der beiden Operanden vom Typ unsigned ist, wird der andere nach unsigned konvertiert.

8.	In allen anderen Fällen sind beide Operanden vom Typ int.

Das Ergebnis des Ausdrucks ist von demselben Typ wie die beiden Operanden.

Verfahren für arithmetische Standard-Umwandlungen

Typ	Konvertierung in	Verfahren

char	int	Null- oder vorzeichenerweitert (abhängig vom voreingestellten char-Typ)

unsigned char	int	Höherwertiges Byte immer Null

signed char	int	Vorzeichenerweitert (immer)

short	int	Wert unverändert, vorzeichenerweitert

unsigned short	unsigned int	Wert unverändert, nullerweitert

enum	int	Unverändert

Besondere Umwandlungen bei char, int und enum

Hinweis:	Die in diesem Abschnitt erläuterten Konvertierungen sind spezifisch für C++Builder.

Wird ein vorzeichenbehaftetes Objekt (zum Beispiel eine Variable) einem Integerobjekt zugewiesen, erfolgt eine automatische Erweiterung des Vorzeichens. Objekte vom Type signed char erhalten immer eine Erweiterung des Vorzeichens; Objekte vom Typ unsigned char setzen immer das höherwertige Byte auf Null, wenn sie nach int konvertiert werden.

Bei Konvertierung eines längeren Integertyps in einen kürzeren werden die höherwertigen Bits abgeschnitten und die niederwertigen bleiben unverändert. Bei der Konvertierung eines kürzeren Integertyps in einen längeren wird entweder vorzeichenerweitert oder die zusätzlichen Bits des neuen Wertes werden auf Null gesetzt, abhängig davon, ob der kürzere Typ signed oder unsigned ist.


Initialisierung

Siehe auch

Initialisierer setzen den Anfangswert, der in einem Objekt (Variable, Array, Struktur usw.) gespeichert wird. Wenn Sie ein Objekt von statischer Lebensdauer nicht initialisieren, wird es per Voreinstellung initialisiert:

Es wird auf 0 gesetzt, wenn es zu einem arithmetischen Typ gehört.

Es wird auf NULL gesetzt, wenn es ein Zeigertyp ist.

Hinweis:	Ist das Objekt von automatischer Lebensdauer, ist sein Wert unbestimmt.

Syntax für Initialisierer

Initialisierer

= Ausdruck

= {Initialisierer-Liste} <,>}

(Ausdrucksliste)

Initialisierer-Liste

Ausdruck

Initialisierer-Liste, Ausdruck

{ Initialisierer-Liste} <,>}

Regeln für Initialisierer

Die Anzahl der Initialisierer in der Initialisierer-Liste darf nicht größer sein als die Anzahl der Objekte, die initialisiert werden sollen.

Nur Objekte (zum Beispiel ein Array) mit unbekannter Größe können initialisiert werden.

In C (aber nicht in C++) müssen alle Ausdrücke Konstanten sein, wenn sie auf einer der folgenden Positionen stehen:

In einer Initialisierung für ein Objekt mit statischer Lebensdauer

In einer Initialisierer-Liste für ein Array, eine Struktur oder Variante (Ausdrücke mit sizeof sind ebenfalls erlaubt)

Wenn eine Deklaration für einen Bezeichner den Gültigkeitsbereich Block hat und der Bezeichner über externe oder interne Bindung verfügt, darf die Deklaration keinen Initialisierer für den Bezeichner haben.

Stehen in einer mit geschweiften Klammern eingeschlossenen Liste weniger Initialisierer, als Elemente einer Struktur vorhanden sind, werden die übrigbleibenden Elemente implizit wie Objekte mit statischer Speicherdauer initialisiert.

Skalare Datentypen werden mit einem einzigen Ausdruck initialisiert, der optional in geschweiften Klammern stehen kann. Der Anfangswert des Objekts entspricht dem des Ausdrucks; in Bezug auf Typ und Umwandlung treffen dieselben Beschränkungen zu wie bei einfachen Zuweisungen.

Bei Varianten initialisiert ein in geschweiften Klammern stehender Initialisierer das Element, das als erstes in der Liste der Variantendeklaration steht. Strukturen und Varianten von automatischen Speicherklassen erfordern einen der folgenden Initialisierer:

Eine Initialisierer-Liste (wie in Arrays, Strukturen und Varianten beschrieben)

Einen einfachen Ausdruck mit kompatiblem Varianten- oder Strukturtyp. In diesem Fall entspricht der Anfangswert des Objekts dem des Ausdrucks.

Arrays, Strukturen und Varianten

Sie können Arrays und Strukturen (zum Beispiel bei der Deklaration) mit einer in geschweiften Klammern stehenden Liste von Initialisierern für Elemente des in Frage kommenden Objekts initialisieren. Die Initialisierer werden in aufsteigender Reihenfolge der Array-Indizes oder Elemente angegeben. Varianten initialisieren Sie mit einem in geschweiften Klammern stehenden Initialisierer für das erste Element der Variante. So können Sie zum Beispiel ein Array days deklarieren, das zählen soll, wie oft jeder Wochentag in einem Monat vorkommt (und dabei unterstellen, daß jeder Tag wenigstens einmal vorkommt):

int days[7] = { 1, 1, 1, 1, 1, 1, 1 }

Die folgenden Regeln gelten bei der Initialisierung von Zeichen und "Wide-Character"-Arrays:

Zeichen-Arrays können mit einem String-Literal (optional in geschweiften Klammern eingeschlossen) initialisiert werden. Jedes Zeichen des Strings (einschließlich der abschließenden Null) initialisiert nacheinander ein Element in dem Array. So können Sie zum Beispiel folgende Deklaration angeben:

char name[] = { "Unbekannt" };

In dieser Deklaration wird ein Array mit acht Elementen definiert. Die Elemente sind 'U' (für name[0]), 'n' (für name[1]) usw. (einschließlich der abschließenden Null).

Sie können ein "Wide-Character"-Array (das kompatibel mit wchar_t ist) mit einem "wideString" initialisieren, der in geschweiften Klammern stehen kann. Wie bei Zeichen-Arrays initialisieren die Codes des "wideString"-Literals nacheinander die Elemente des Arrays.

Im folgenden sehen Sie ein Beispiel für die Initialisierung einer Struktur:

struct mystruct {

   int i;

   char str[21];

   double d;

   } s = { 20, "Borland", 3.141 };

Komplexe Elemente einer Struktur, wie Arrays oder Strukturen, können mit entsprechenden Ausdrücken innerhalb geschachtelter geschweifter Klammern initialisiert werden.


Deklarationen und Deklaratoren

Siehe auch

Eine Deklaration ist eine Liste mit Namen. Diese Namen werden manchmal Deklaratoren oder Bezeichner genannt. Die Deklaration beginnt mit den optionalen Speicherklassen-Bezeichnern, den Typ-Bezeichnern und anderen Modifizierern. Die Bezeichner werden durch Kommata voneinander getrennt. Die Liste wird durch ein Semikolon abgeschlossen.

Einfache Deklarationen von Variablenbezeichnern haben folgendes Muster:

Datentyp var1 <=init1>, var2 <=init2>, ...;

wobei var1, var2,... eine beliebige Folge verschiedener Bezeichner darstellt, die zusätzlich Initialisierer enthalten kann. Alle Variablen sind vom Typ Datentyp. Beispielsweise erzeugt

int x = 1, y = 2;

zwei Integer-Variablen x und y (und initialisiert sie mit den Werten 1 beziehungsweise 2).

Es handelt sich hierbei um definierende Deklarationen, das heißt, es erfolgt eine Speicherzuweisung und die optionalen Initialisierer werden angewendet.

Als Initialisierer für ein automatisches Objekt kann jeder zulässige Ausdruck verwendet werden, der zu einem zuweisungskompatiblen Wert für den Typ der betreffenden Variable ausgewertet werden kann. Die Initialisierer für statische Objekte müssen Konstanten oder Konstantenausdrücke sein.

In C++ kann jeder Ausdruck mit Konstanten und bereits deklarierten Variablen und Funktionen als Initialisierer für ein statisches Objekt verwendet werden.

Das Format des Deklarators zeigt an, wie der deklarierte Name in einem Ausdruck interpretiert werden soll. Wenn type ein beliebiger Typ, der Speicherklassen-Bezeichner ein beliebiger Speicherklassen-Bezeichner ist, und wenn D1 und D2 zwei Deklaratoren sind, dann gibt die Deklaration

Speicherklassen-Bezeichner type D1, D2;

an, daß D1 und D2 jedesmal als Objekt des Typs type und der Speicherklasse Speicherklassen-Bezeichner behandelt werden sollen, wenn sie in einem Ausdruck vorkommen. Der in den Deklarator eingebettete Typ des Namens ist ein Ausdruck, der type beinhaltet, wie "type", "Zeiger auf type", "Array des type", "Funktion liefert type" oder "Zeiger auf die Funktion, die type liefert" usw. enthält.

Zum Beispiel kann jeder in der Tabelle Beispiele für die Deklaratorsyntax enthaltene Deklarator in solchen Ausdrücken als R-Wert (oder in einigen Fällen als L-Wert) verwendet werden, in denen ein einfaches Objekt vom Typ int gültig ist. Der Typ der eingebetteten Bezeichner wird folgendermaßen von deren Deklaration abgeleitet:

Weitere Beispiele für die Deklarationssyntax

Deklaratorsyntax	Implizierter Typ von Name	Beispiel

type name;	type	int count;

type name[];	(offenes) Array von type	int count[];

type name[3];	Festes Array von drei Elementen,	int count[3];
vom Typ type (name[0], name[1] und name[2])

type *name;	Zeiger auf type	int *count;

type *name[];	(offenes) Array von Zeigern auf type	int *count[];

type *(name[]);	wie oben	int *(count[]);

type (*name)[];	Zeiger auf ein (offenes) Array von type	int (*count) [];

type &name;	Referenz auf type (nur C++)	int &count;

type name();	Funktion, die type zurückgibt	int count();

type *name();	Funktion, die Zeiger auf type zurückgibt	int *count();

type *(name());	wie oben	int *(count());

type (*name)();	Zeiger auf Funktion, die type zurückgibt	int (*count) ();

Die Klammern in (*name)[] und (*name)() sind erforderlich, weil sowohl der Array-Deklarator [ ] als auch der Funktionsdeklarator ( ) einen höheren Rang besitzen als der Zeiger-Deklarator *. Die Klammern in *(name[]) sind nicht unbedingt erforderlich.

Hinweis:	Informationen über die Deklaratorsyntax finden Sie in C++BuilderDeklarationssyntax. Die Definition umfaßt sowohl Bezeichner als auch Funktionsdeklaratoren.


Speicherklassenspezifizierer

Speicherklassenspezifizierer werden auch Typspezifizierer genannt. Sie bestimmen den Speicherort (Datensegment, Register, Heap oder Stack) eines Objektes sowie seine Lebensdauer (die sich entweder über die gesamte Laufzeit des Programms oder nur auf die Ausführung eines bestimmten Programmblocks erstrecken kann). Die Speicherklasse kann durch Syntaxelemente innerhalb einer Deklaration, durch Plazierung der Deklaration im Quelltext oder durch beides festgelegt werden.

Das Schlüsselwort mutable hat keine Auswirkungen auf die Lebensdauer der Klassenelemente, auf die es angewendet wird.

Bei C++Builder gibt es folgende Speicherklassenspezifizierer:

auto	register

__declspec	static

extern	typedef

mutable


Modifizierer von Variablen

Siehe auch

Neben den Schlüsselwörtern zur Spezifizierung einer Speicherklasse können in einer Deklaration Modifizierer eingesetzt werden, um die zum Objekt/Bezeichner gehörende Speicherbelegung zu ändern. Die in C++Builder zur Verfügung stehenden Modifizierer sind in C++Builder Modifizierer aufgeführt.

Aufrufkonventionen für andere Programmiersprachen

C++Builder ermöglicht Ihren Programmen den einfachen Aufruf von Routinen, die in anderen Sprachen geschrieben sind (und umgekehrt). Bei der Arbeit mit verschiedenen Sprachen müssen Sie zwei Probleme lösen: Bezeichner und Parameterübergabe.

In C++Builder werden alle globalen Bezeichner in unveränderter Schreibweise (Groß- oder Kleinbuchstaben bzw. eine Kombination aus beidem) mit vorangestelltem Unterstrich ("_") gespeichert. Diese Voreinstellung kann umgangen werden, indem der Schalter -u aktiviert oder die in der IDE gesetzte Compiler-Option deaktiviert wird.

Hinweis:	Im Thema Bindung wird die Verwendung von extern erläutert, mit dem Sie C-Namen aus einem C++ Programm referenzieren können.

In Aufrufkonventionen finden Sie eine Erklärung der Auswirkungen eines Modifizierers auf eine aufgerufene Funktion. Die Tabelle enthält für jeden Modifizierer die Reihenfolge, in der die Funktionsparameter auf den Stack gelegt werden. Weiterhin zeigt die Tabelle, ob das aufrufende Programm (der Aufrufende) oder die aufgerufene Funktion (der Aufgerufene) für die Abholung der Parameter vom Stack verantwortlich ist. Schließlich sind in der Tabelle die Auswirkungen auf die Namen globaler Funktionen aufgezeichnet.

Konventionen

Modifizierer	Push-Parameter	Pop-Parameter	Namensveränderung

_ _cdecl1	Rechts zuerst	Aufrufender	'_' vorangestellt

_ _fastcall	Links zuerst	Aufgerufener	'@' vorangestellt

_ _pascal	Links zuerst	Aufgerufener	Großbuchstaben

_ _stdcall	Rechts zuerst	Aufgerufener	Keine Veränderung

1. Dies ist die Standardeinstellung.

Hinweis:	_ _fastcall und _ _stdcall werden für die Namensergänzung verwendet. Sehen Sie dazu auch unter der Option -VC nach.


const

Syntax

const <Variablenname> [ = <Wert> ] ;

<Funktionsname> ( const <Typ>*<Variablenname> ;)

<Funktionsname> const;

Beschreibung

Verwenden Sie den Modifizierer const, um Änderungen des Werts einer Variable zu verhindern.

Verwenden Sie den Modifizierer const, um einer Variablen einen vom Programm nicht mehr veränderbaren Anfangswert zu geben. Jede spätere Wertzuweisung für eine const-Variable führt zu einer Fehlermeldung des Compilers.

Ein Zeiger const kann nicht verändert werden, obwohl das Objekt, das auf diesen Zeiger zeigt, verändert werden kann. Betrachten Sie dazu die folgenden Beispiele.

  const float pi   = 3.14;

  const  maxint  = 12345;   // When used by itself, const is equivalent to int.

  char *const str1 = "Hello, world";           // Ein konstanter Zeiger

  char const *str2 = "Borland International"; // Ein Zeiger auf einen konstanten String.

Wenn diese Deklarationen vorgegeben sind, sind die folgenden Anweisungen gültig.

pi   = 3.0;         // Weist einer Konstante einen Wert zu.

i     = maxint++;   // Inkrementiert eine Konstante.

str1 = "Hi, there!" // Laesst str1 auf irgendetwas zeigen.

Verwendung des Schlüsselworts const in C++ Programmen:

In C++ ist die Verwendbarkeit von const auf Klassen und Elementfunktionen erweitert. Wenn Sie in einer C++ Klassendefinition den Modifizierer const hinter die Deklaration einer Elementfunktion setzen, so kann diese Funktion keine Daten der Klasse verändern.

Ein Klassenobjekt, das mit dem Schlüsselwort const definiert worden ist, kann nur solche Elementfunktionen aufrufen, die ebenfalls mit const definiert worden sind. Der Versuch, eine nicht mit const definierte Elementfunktion aufzurufen, führt zu der Fehlermeldung non-constant function class-name::member-function-name() (Nicht-konstante Funktion Klassenname::Elementfunktionsname()) Durch diese Verwendungsmöglichkeit des Schlüsselworts const bietet C++ zusätzliche Sicherheit; der Compiler unterstützt diese Methode aber nicht automatisch.

Warnung:	Eine const-Variable kann indirekt über einen Zeiger verändert werden, wie in folgendem Beispiel:

*(int *)&mein_alter = 35;

Wird der Modifizierer const für einen Zeigerparameter in der Parameterliste einer Funktion benutzt, so kann die Funktion die Variable, auf die der Zeiger weist, nicht verändern. Beispiel dazu:

int printf (const char *format, ...);

Die Funktion printf kann nun den Formatstring nicht mehr verändern.


volatile

Siehe auch	Schlüsselwörter

Syntax

volatile < datendefinition > ;

Beschreibung

Der Modifizierer volatile teilt dem Compiler mit, daß eine Variable möglicherweise von einer Hintergrundroutine, einer Interruptroutine oder einem I/O-Port verändert wird. Die Deklaration eines Objektes als volatile warnt den Compiler, keine den Wert des Objektes betreffenden Zusicherungen zu machen, so lange die Ausdrücke, in denen es erscheint, evaluiert werden. Denn der Wert könnte sich jeden Augenblick ändern. Es hält den Compiler auch davon ab, aus einer Variablen eine Registervariable zu machen.

volatile int ticks;

void timer( ) {

   ticks++;

}

void wait (int interval) {

   ticks = 0;

   while (ticks < interval);  // Do nothing

}

Die Routinen in diesem Beispiel (sicherstellend, daß timer passend mit einem Interrupt der Hardware-Uhr verbunden ist) implementieren ein zeitliches Warten auf Schläge, die durch das Argument interval spezifiziert sind. Ein besonders optimierter Compiler könnte den Wert von ticks innerhalb des Tests der Schleife while nicht laden, seit die Schleife den Wert von ticks nicht ändert.

Hinweis:	C++ erweitert volatile auf Klassen und Elementfunktionen. Wenn Sie ein Objekt volatile deklariert haben, können Sie ausschließlich seine volatilen Elementfunktionen verwenden.


cdecl, _cdecl, und __cdecl
cdecl, _cdecl,  _ _cdecl

Beispiel	Schlüsselwörter

Syntax

cdecl <daten-/funktions-definition> ;

_cdecl <daten-/funktions-definition> ;

__cdecl <daten-/funktions-definition> ;

Beschreibung

Die Modifizierer cdecl, _cdecl und __cdecl werden benutzt, um eine Variable oder eine Funktion gemäß den C-Namenskonventionen (Unterscheidung von Groß-/Kleinschreibung, als erstes Zeichen wird ein Unterstrich hinzugefügt) zu deklarieren. Wenn Sie einem Funktionsnamen cdecl, _cdecl oder _ _cdecl voranstellen, so wird damit auch die Art der Parameterübergabe beeinflußt (Der letzte Parameter kommt zuerst auf den Stack, das aufrufende Programm bereinigt den Stack). De Modifizierer _ _cdecl überschreibt die Compilier-Dierektiven und DIE-Optionen.

cdecl, _cdecl und __cdecl sind spezifische Schlüsselwörter von C++Builder.


pascal, _pascal, und __pascal
pascal, _pascal,  _ _pascal

Schlüsselwörter

Syntax

pascal <datendefinition/funktionsdefinition> ;

_pascal <datendefinition/funktionsdefinition> ;

__pascal <datendefinition/funktionsdefinition> ;

Beschreibung

Die Schlüsselwörter pascal, _pascal und __pascal dienen dazu, eine Variable oder Funktion mit den Namenskonventionen der Sprache Pascal (Namen werden in Großbuchstaben geschrieben) zu definieren.

Wenn eines dieser Schlüsselworte in einer Funktionsdefinition benutzt wird, so wird dadurch außerdem festgelegt, daß die Parameterübergabe nach Pascal-Konvention erfolgt (Der erste Parameter kommt zuerst auf den Stack, die aufrufende Funktion bereinigt den Stack).

In C++ Programmen werden die Namen von Funktionen, die mit den Modifizierer pascal deklariert sind, trotzdem verändert.


_stdcall und __stdcall
_stdcall,  __stdcall

Schlüsselwörter

Syntax

__stdcall <funktionsname>

_stdcall <funktionsname>

Beschreibung

Die Schlüsselwörter _stdcall und __stdcall veranlassen den Compiler, für Funktionsaufrufe Befehle gemäß der Standard-Aufrufkonvention zu erzeugen. Solche Funktionsaufrufe sind schneller und benötigen weniger Speicherplatz. In Funktionsaufrufen müssen die Argumente nach Anzahl und Typ korrekt angegeben werden; dies steht im Gegensatz zur normalen C-Syntax, bei der eine variable Anzahl von Argumenten erlaubt ist. Derartige Funktionen halten sich an die Standardkonvention bei der Übergabe von Komponenten unter WIN32.

Hinweis:	Der Modifizierer __stdcall ist Gegenstand für das Ergängen von Namen. Siehe die Beschreibung der Option -VC.


_fastcall und __fastcall
_fastcall, _ _fastcall

Siehe auch	Schlüsselwörter

Syntax

rückgabewert _fastcall funktionsname(parameterliste)

rückgabewert __fastcall funktionsname(parameterliste)

Beschreibung

Verwenden Sie den Modifizierer __fastcall um Funktionen zu deklarieren, die erfordern, daß Parameter in Registern übergeben werden. Die ersten drei Parameter werden (von links nach rechts) in EAX, EBX, und EDX übergeben, falls sie in das Register passen. Die Register werden nicht verwendet, wenn der Parameter ein Gleitkommatyp oder ein struct-Typ ist.

Alle Formular-Klassenelementfunktionen müssen die __fastcall-Konvention verwenden.

Der Compiler behandelt diese Aufrufkonvention als Angabe einer anderen Sprache, ähnlich wie bei _cdecl und _pascal.

Funktionen, die mit _cdecl oder _pascal deklariert werden, können nicht gleichzeitig auch mit _fastcall deklariert werden, weil sie die Parameter über den Stack übergeben. Weiterhin können die Modifizierer __fastcall nicht zusammen mit _export verwendet werden.

Der Compiler stellt den Namen von _fastcall-Funktionen das At-Zeichen ("@") voran. Dies gilt sowohl für nicht veränderte C-Funktionsnamen als auch für veränderte C++ Funktionsnamen.

Hinweis:	Der Modifizierer __fastcall ist dem Ergängen von Namen ausgesetzt. Eine entsprechende Beschreibung finden Sie unter der Option -VC.


Variablen vom Typ Multithread

Schlüsselwörter

Das Schlüsselwort _ _thread wird verwendet, um in Programmen mit Multithread eine eindeutige Kopie von globalen und statischen Klassenvariablen zu bewahren. Jeder Programm-Thread enthält für jeden Thread-Prozeß eine eigene Kopie der Variablen vom Typ _ _thread.

Die Syntax lautet: Type __thread variable__name. Beispielsweise deklariert

 int __thread x;

eine Variable vom Typ integer, die global aber ausschließlich zu den Threads im Programm gehören wird, bei denen die Anweisung auftritt.

Der Modifizierer _ _thread kann mit globalen (Datei-Gültigkeitsbereich) und statischen Variablen verwendet werden. Der Modifizierer kann nicht mit Zeigern und Funktionen verwendet werden (Sie können jedoch Zeiger auf Objekte vom Typ _ _thread haben). Ein Programmelement, das Laufzeitinitialisierung oder Laufzeitbeendigung benötigt, kann nicht als Typ _ _thread deklariert werden. Die folgenden Deklarationen benötigen Laufzeitinitialisierung und sind damit unzulässig.

int f( );

int __thread x = f( );   // unzulässig

Die Instantiierung einer Klasse mit anwenderdefiniertem Konstruktor und Destruktor benötigt Laufzeitinitialisierung und ist damit unzulässig.

class X  {

   X( );

   ~X( );

};

X __thread myclass;   // unzulässig


Modifizierer für Funktionstypen
Funktions-Modifizierer

Siehe auch

Dieser Abschnitt enthält eine Beschreibung der Funktions-Modifizierer in C++Builder.

Sie können die Modifizierer _ _export und _ _import verwenden, um Funktionen zu ändern.

In 32-Bit-Programmen kann das Schlüsselwort für Klassen-, Funktionen- und Variablen-Deklarationen angewendet werden.

Der Modifizierer _ _export bestimmt, daß eine Funktion aus Windows exportierbar ist. Der Modifizierer __import stellt eine Funktion anderen Windows-Programmen zur Verfügung. Die Schlüsselwörter werden in einer ausführbaren Datei (wenn Sie nicht Smart Callbacks verwenden) oder in einer DLL eingesetzt.

Wird eine Funktion mit dem Modifizierer __fastcall deklariert, erhält sie einen anderen Namen. Der Compiler versieht den __fastcall-Funktionsnamen mit einem vorangestellten @, unabhängig davon, ob es sich um einen nicht ergänzten C-Funktionsnamen oder einen ergänzten C++ Funktionsnamen handelt.

C++Builder Modifizierer

Modifizierer	Verwendet bei	Beschreibung

const1	Variablen	Verhindert Veränderungen an einem Objekt.

volatile1	Variablen	Verhindert die Registerzuweisung und andere Optimierungsmaßnahmen. Warnt den Compiler, daß das Objekt während der Auswertung von außen geändert werden kann.

_ _cdecl2	Funktionen	Erzwingt die C-Konvention für die Übergabe von Argumenten. Betrifft den Linker und beim Linken vergebene Namen

_ _cdecl2	Variablen	Erzwingt den führenden Unterstrich und die Unterscheidung von Groß- und Kleinbuchstaben bei globalen Bezeichnern

_ _pascal	Funktionen	Erzwingt die Pascal-Konvention für die Übergabe von Argumenten. Betrifft den Linker und die beim Linken vergebenen Namen.

_ _pascal	Variablen	Zwischen Groß- und Kleinbuchstaben wird bei globalen Bezeichnern nicht unterschieden und es wird kein führender Unterstrich hinzugefügt.

_ _export	Funktionen/Klassen	Teilt dem Compiler mit, welche Funktionen oder Klassen exportiert werden sollen.

_ _import	Funktionen/Klassen	Teilt dem Compiler mit, welche Funktionen oder Klassen importiert werden sollen. (In 16-Bit-Programmen kann dieses Schlüsselwort nur für Klassendeklarationen verwendet werden.)

_ _fastcall	Funktionen	Erzwingt die Registerkonvention bei der Übergabe von Parametern. Betrifft den Linker und beim Linken vergebene Namen.

_ _stdcall	Funktionen	Erzwingt die Einhaltung des Standard der WIN32-Konvention zur Argumentübergabe.

1	C++ erweitert const und volatile beim Einfügen von Klassen und Elementfunktionen.

2	Voreinstellung.


Zeiger

Siehe auch

Zeiger lassen sich in zwei Kategorien einteilen: Zeiger auf Objekte und Zeiger auf Funktionen. Beide Zeigertypen sind spezielle Objekte, die Speicheradressen speichern.

Die beiden Zeigerklassen haben unterschiedliche Eigenschaften, Zwecke und Regeln für die Handhabung, obwohl man teilweise die gleichen C++Builder Operationen auf sie anwenden kann. Generell kann gesagt werden, daß Zeiger auf Funktionen für den Zugriff auf Funktionen und für die Übergabe von Funktionen als Argumente an andere Funktionen verwendet werden. Arithmetische Berechnungen mit Zeigern auf Funktionen sind nicht zulässig. Zeiger auf Objekte können jedoch regulär inkrementiert oder dekrementiert werden, wenn Arrays oder komplexe Datenstrukturen im Speicher abgesucht werden.

Obwohl Zeiger Zahlen beinhalten, die fast dieselben Eigenschaften wie vorzeichenlose Integerzahlen aufweisen, gelten für diese eigene Regeln und Restriktionen in Bezug auf Zuweisungen, Konvertierung und Arithmetik. Die Beispiele in den nächsten Abschnitten illustrieren diese Regeln und Restriktionen.

Hinweis:	Eine eingehende Erörterung von Referenzen und dem Vorgang der Dereferenzierung finden Sie unter dem Thema Referenzen.


Zeiger auf Objekte

Siehe auch

Ein Zeiger des Typs "Zeiger auf Objekt vom Typ type" enthält die Adresse eines Objekts (d.h., er zeigt auf das Objekt) vom Typ type. Da Zeiger selbst Objekte sind, können auch Zeiger auf Zeiger zeigen (usw.). Arrays, Strukturen, Varianten und Klassen sind weitere Objekte, die häufig über Zeiger angesprochen werden.


Zeiger auf Funktionen

Siehe auch

Einen Zeiger auf eine Funktion kann man sich als Adresse – normalerweise in einem Code-Segment – vorstellen, an deren Position der ausführbare Code für diese Funktion gespeichert ist. Dies ist die Adresse, der die Kontrolle übergeben wird, wenn diese Funktion aufgerufen wird. Die Größe und Position Ihres Code-Segments wird vom Speichermodell bestimmt, das seinerseits die Größe der Funktionszeiger festlegt.

Ein Zeiger auf eine Funktion hat den Typ "Zeiger auf eine Funktion, die type zurückliefert", wobei type der Rückgabetyp der Funktion ist. Beispiel:

void (*func)();

In C++ ist dies ein Zeiger auf eine Funktion ohne Argumente, die void zurückliefert. In C ist dies ein Zeiger auf eine Funktion mit einer unbestimmten Anzahl von Argumenten, die void zurückliefert. In dem folgenden Beispiel

void (*func)(int);

ist *func ein Zeiger auf eine Funktion, die ein int-Argument entgegennimmt und void zurückliefert.

In C++ kann über einen solchen Zeiger auf statische Elementfunktionen zugegriffen werden. Zeiger auf Klassenelemente müssen Zeiger-auf-Elementoperatoren verwenden. Weitere Informationen hierzu finden Sie unter static_cast.


Zeigerdeklarationen

Siehe auch

Ein Zeiger muß als auf einen bestimmten Typ zeigend deklariert werden, auch wenn dieser Typ void ist (also ein Zeiger auf ein beliebiges Objekt). Sobald ein Zeiger einmal deklariert ist, kann man ihn auch auf ein Objekt mit einem anderen Typ zeigen lassen. In C++Builder können Sie Zeiger ohne Typumwandlung (Typecasting) neu zuordnen. Sie erhalten dabei jedoch eine Warnung vom Compiler, außer wenn der Zeiger ursprünglich als Zeiger auf void deklariert war. In C, aber nicht in C++, können Sie einen void*-Zeiger einem nicht-void*-Zeiger zuweisen. Weitere Informationen finden Sie unter  void.

Warnung:	Sie müssen Zeiger vor ihrer Verwendung initialisieren.

Wenn type ein vordefinierter oder benutzerdefinierter Typ ist, einschließlich void, dann legt die Deklaration

type *ptr;   /* Uninitialisierter Zeiger */

ptr als Zeiger vom Typ "Zeiger auf type" fest. Alle Regeln für Gültigkeitsbereich, Dauer und Sichtbarkeit treffen auf das gerade deklarierte Objekt ptr zu.

Ein NULL-Zeigerwert ist eine Adresse, die sich garantiert von jedem in einem Programm verwendeten gültigen Zeiger unterscheidet. Die Zuweisung der Integerkonstante 0 ordnet einem Zeiger einen NULL-Zeigerwert zu.

Aus Gründen der Lesbarkeit kann auch das Kürzel NULL (definiert in den Include-Dateien der Standardbibliothek, z.B. in stdio.h) verwendet werden. Alle Zeiger können erfolgreich auf Gleichheit oder Ungleichheit zu NULL überprüft werden.

Der Zeigertyp "Zeiger auf void" darf nicht mit dem NULL-Zeiger verwechselt werden. Die Deklaration

void *vptr;

deklariert vptr als generischen Zeiger, dem jeder "Zeiger auf type"-Wert, einschließlich NULL, zugewiesen werden kann. Zuweisungen ohne geeignete Typumwandlung zwischen einem "Zeiger auf type1" und einem "Zeiger auf type2" – wobei type1 und type2 unterschiedliche Typen sind – können eine Compiler-Warnung oder einen Fehler hervorrufen. Wenn type1 eine Funktion und type2 keine Funktion ist (oder umgekehrt), dann sind Zeigerzuweisungen nicht erlaubt. Ist type1 ein Zeiger auf void, dann ist eine Typumwandlung nicht notwendig. Ist type2 ein Zeiger auf void, so ist in C ebenfalls keine Typumwandlung notwendig.


Zeigerkonstanten

Siehe auch

Ein Zeiger oder das Objekt, auf das gezeigt wird, kann mit dem const-Modifizierer deklariert werden. Einem Objekt, das als const deklariert ist, kann nichts zugewiesen werden. Es ist auch nicht zulässig, einen Zeiger anzulegen, der diese Regel verletzt. Betrachten Sie folgende Beispiele:

int i;                       // i ist ein int

int * pi;                    // pi ist ein Zeiger auf einen int (uninitialisiert)

int * const cp = &i;         // cp ist ein konstanter Zeiger auf einen int

const int ci = 7;            // ci ist ein konstanter int

const int * pci;             // pci ist ein Zeiger auf einen konstanten int

const int * const cpc = &ci; // cpc ist ein konstanter Zeiger auf einen

                             // konstanten int

Folgende Zuweisungen sind zulässig:

i = ci;                     // const-int zu int

*cp = ci;                   // const-int zu Objekt, auf das ein

                            // konstanter Zeiger zeigt

++pci;                      // Inkrementieren eines Zeigers auf ein const

pci = cpc;                  // konstanter Zeiger auf einen const

                            // zu Zeiger auf const

Folgende Zuweisungen sind nicht zulässig:

ci = 0;                     // Falsch: Zuweisung zu const-int nicht erlaubt

ci--;                       // Falsch: const-int darf nicht geändert werden

*pci = 3;                   // Falsch: Zuweisung zu Objekt, auf das ein

                            // Zeiger auf const zeigt, nicht erlaubt

cp = &ci;                   // Falsch: Zuweisung zu einem konstanten Zeiger nicht

                            // erlaubt, auch wenn der Wert unverändert bleibt

cpc++;                      // Falsch: konstanter Zeiger darf nicht geändert
                          // werden

pi = pci;                   // Falsch: Wäre diese Zuweisung möglich, könnten

                            // Sie dem konstanten Wert *pci über *pi einen

                            // Wert zuweisen.

Ähnliche Regeln gelten für den Modifizierer volatile. Beachten Sie, daß const und volatile als Modifizierer für denselben Bezeichner verwendet werden können.


Zeigerarithmetik

Siehe auch

Die Zeigerarithmetik beschränkt sich auf Addition, Subtraktion und Vergleich. Arithmetische Operationen bei Zeigern vom Typ "Zeiger auf type" berücksichtigen automatisch die Größe des Typs type, d.h., die Anzahl der Bytes, die für das Speichern des Objeks type notwendig sind.

Die intern ausgeführte Zeigerarithmetik richtet sich nach dem aktuellen Speichermodell und dem Vorhandensein von überschreibenden Zeiger-Modifizierern.

Bei Berechnungen mit Zeigern wird davon ausgegangen, daß der Zeiger auf ein Array von Objekten zeigt. So wird ein Zeiger, wenn er als Zeiger auf den Typ type deklariert ist, bei Hinzufügen eines Integerwertes um die Anzahl der Objekte vom Typ type weitergesetzt. Wenn type die Größe 10 (Byte) hat und der Integerwert 5 zu einem Zeiger auf type hinzukommt, erhöht sich der Wert der Speicheradresse um 50 (Bytes). Die Differenz ist die Anzahl der Array-Elemente, um die sich die beiden Zeiger voneinander unterscheiden. Wenn zum Beispiel ptr1 auf das dritte Element eines Array und ptr2 auf das zehnte Element des Arrays zeigt, dann ist das Ergebnis von ptr2 – ptr1 gleich 7.

Die Differenz zwischen zwei Zeigern ergibt nur einen sinnvollen Wert, wenn beide Zeiger in das gleiche Array verweisen.

Wenn ein Integerwert zu einem "Zeiger auf type" addiert oder von ihm subtrahiert wird, ist das Ergebnis ebenfalls  vom Typ "Zeiger auf type".

Es gibt natürlich keinen "Zeiger auf das Element nach dem letzten Element", aber ein Zeiger kann diesen Wert annehmen. Wenn P auf das letzte Array-Element zeigt, ist P + 1 zulässig, aber P + 2 undefiniert. Wenn P auf das Element nach dem letzten Array-Element zeigt, ist P – 1 zulässig und ergibt einen Zeiger auf das letzte Element. Die Anwendung des Zeigeroperators * auf einen "Zeiger auf das Element nach dem letzten Element" führt jedoch zu undefiniertem Verhalten.

Sie können sich P + n als Inkrementierung des Zeigers um (n * sizeof(type)) Bytes vorstellen, solange der Zeiger innerhalb des zulässigen Bereichs bleibt (vom ersten Element bis zu dem Element nach dem letzten Element).

Wenn zwei Zeiger auf Elemente desselben Arrays subtrahiert werden, erhält man einen Integerwert vom Typ ptrdiff_t, der in stddef.h definiert ist. Dieser Wert stellt die Differenz zwischen den Indizes der beiden referenzierten Elemente dar, vorausgesetzt, sie liegt im Bereich von ptrdiff_t. Im Ausdruck P1 – P2, wobei P1 und P2 Zeiger auf den Typ type sind (oder Zeiger auf den qualifizierten Typ type), müssen P1 und P2 auf existierende Elemente zeigen oder auf das Element nach dem letzten Element. Wenn P1 auf das i-te Element und P2 auf das j-te Element zeigt, hat P1 – P2 den Wert (i – j).


Zeigerumwandlungen

Siehe auch

Zeigertypen können durch Typumwandlung in andere Zeigertypen konvertiert werden:

char *str;

int *ip;

str = (char *)ip;

Allgemeiner ausgedrückt heißt dies: (type*) konvertiert einen Zeiger in den Typ "Zeiger auf type". Weitere Informationen über den Typumwandlungsmechanismus in C++ finden Sie unter C++ spezifisch.


C++ Referenzdeklarationen

Siehe auch

C++ Referenztypen sind eng mit den Zeigertypen verwandt. Referenztypen erzeugen Stellvertreter für Objekte und erlauben die Übergabe von Argumenten an Funktionen per Referenz. In C erfolgt die Argumentübergabe nur über Wertparameter. In C++ können Sie Argumente mit Wertparametern oder Referenzparametern übergeben. Unter Referenzierung erhalten Sie detaillierte Informationen.


Arrays

Siehe auch

Die Deklaration

type Deklarator [<Konstantenausdruck>]

deklariert ein Array, das sich aus den Elementen des Typs type zusammensetzt. Ein Array besteht aus einem zusammenhängenden Speicherbereich, der so groß ist, daß alle Elemente darin Platz finden.

Wenn ein Array-Deklarator einen Ausdruck beinhaltet, muß dieser eine positive Integerkonstante ergeben. Der Wert bezeichnet die Anzahl der Array-Elemente. Jedes Element in einem Array ist numeriert und zwar von 0 bis zur Anzahl der Elemente minus Eins.

Mehrdimensionale Arrays werden durch Deklaration von Arrays des Typs Array aufgebaut. Im folgenden Beispiel wird gezeigt, wie ein zweidimensionales Array erstellt wird. Die Beispiel-Implementierung erzeugt drei Zeilen und fünf Spalten, kann aber sehr leicht so verändert werden, daß dies zur Laufzeit durch Benutzereingaben gesteuert werden kann.

/* DYNAMISCHE SPEICHERRESERVIERUNG FÜR EIN MEHRDIMENSIONALES OBJEKT */

#include <stdio.h>

#include <stdlib.h>

typedef long double TYPE;

typedef TYPE *OBJECT;

unsigned int rows = 3, columns = 5;

void de_allocate(OBJECT);

int main(void) {

  OBJECT matrix;

  unsigned int i, j;

  /* Schritt 1: Erzeugen der Zeilen. */

  matrix = (OBJECT) calloc( rows, sizeof(TYPE *));

  /* Schritt 2: Erzeugen der Spalten. */

  for (i = 0; i < rows; ++i)

    matrix[i] = (TYPE *) calloc( columns, sizeof(TYPE));

    for (i = 0; i < rows; i++)

      for (j = 0; j < columns; j++)

        matrix[i][j] = i + j;    /* INITIALISIEREN */

  for (i = 0; i < rows; ++i) {

    printf("\n\n");

    for (j = 0; j < columns; ++j)

      printf("%5.2Lf", matrix[i][j]);

  de_allocate(matrix);

  return 0;

  }

void de_allocate(OBJECT x) {

  int i;

  for (i = 0; i < rows; i++)    /* Schritt 1: Entfernen der Spalten */

    free(x[i]);

  free(x);    /* Schritt 2: Entfernen der Zeilen. */

  }

Dieser Code liefert folgende Ausgabe:

0.00 1.00 2.00 3.00 4.00

1.00 2.00 3.00 4.00 5.00

2.00 3.00 4.00 5.00 6.00

Unter Umständen hat der erste von mehreren Array-Deklaratoren keinen Ausdruck in den Klammern. Ein derartiges Array ist von unbestimmter Größe. Dies ist dann zulässig, wenn die Größe eines Arrays nicht zur Speicherplatzreservierung erforderlich ist.

Die genaue Größe eines Arrays ist z.B. weder in der extern-Deklaration eines Array noch bei der Angabe eines Array-Funktionsparameters erforderlich. Eine Erweiterung in C++Builder (gegenüber ANSI C) stellt die Möglichkeit dar, ein Array mit unbestimmter Größe als letztes Element in einer Struktur einzusetzen. Ein derartiges Array vergrößert den Umfang der Struktur nur, wenn ein Dummy-Byte eingefügt wird, damit das Array an einer Wortgrenze beginnt. Diesen Strukturen wird normalerweise dynamisch Speicher zugewiesen. Diese Strukturen finden normalerweise bei dynamischer Speichererweiterung Anwendung. Die Größe des aktuellen Arrays muß explizit der Strukturgröße hinzugefügt werden, damit genügend Speicherplatz reserviert wird.

Ein Arraytyp-Ausdruck wird in einen konstanten Zeiger auf das erste Element des Arrays konvertiert, es sei denn, er ist der Operand eines sizeof- oder &-Operators.


Funktionen

Siehe auch

Funktionen sind zentrale Bestandteile der C- und C++ Programmierung. Während bei Sprachen wie Pascal zwischen Prozeduren und Funktionen unterschieden wird, übernehmen in C und C++ die Funktionen beide Aufgabenbereiche.

Elementfunktionen werden manchmal als Methoden bezeichnet.


Deklarationen und Definitionen

Siehe auch

Jedes Programm muß genau eine externe Funktion mit dem Namen main enthalten, die den Beginn des Programms markiert. Funktionen werden normalerweise als Prototypen entweder in Standard- oder benutzerdefinierten Header-Dateien deklariert. Sie können auch innerhalb von Programmdateien deklariert werden. Funktionen sind per Voreinstellung vom Typ extern. Auf sie kann von jeder Datei des Programms zugegriffen werden, aber der Zugriff kann durch den Speicherklassen-Spezifizierer static eingeschränkt werden (siehe auch Bindung).

Funktionen sind in den Quelltextdateien definiert oder werden durch Linken vorkompilierter Bibliotheken zur Verfügung gestellt.

Eine Funktion kann mehrere Male in einem Programm deklariert werden, vorausgesetzt, daß die Deklarationen kompatibel sind. Eine nicht-definierende Funktionsdeklaration, die im Prototyp-Format angegeben wird, liefert C++Builder detaillierte Parameterinformationen. Dadurch kann die Anzahl der Argumente kontrolliert und die Zulässigkeit der Typen überprüft sowie Typumwandlungen vorgenommen werden.

Hinweis:	In C++ müssen immer Funktionsprototypen verwendet werden. Wir raten Ihnen, diese Prototypen auch in C einzusetzen.

In C++ ist nur eine Definition pro Funktion zugelassen. Eine Ausnahme bildet dabei das Überladen von Funktionen. Eventuell vorhandene Deklarationen müssen mit dieser Definition übereinstimmen (der wichtigste Unterschied zwischen einer Definition und einer Deklaration besteht darin, daß die Definition einen Funktionsrumpf besitzt).


Deklarationen und Prototypen

Siehe auch

Bei Kernighan und Ritchie kann eine Funktion implizit durch ihr Vorhandensein in einem Funktionsaufruf oder wie folgt explizit deklariert werden:

<type> func()

wobei type der optionale Rückgabewert mit der Voreinstellung int ist. In C++ bedeutet diese Deklaration <type> func(void). Eine Funktion kann so deklariert werden, daß sie einen beliebigen Typ mit Ausnahme eines Arrays oder einer Funktion liefert. Bei diesem Konzept kann der Compiler nicht überprüfen, ob der Typ oder die Anzahl der Argumente in dem Funktionsaufruf mit der Deklaration übereinstimmen.

Dieses Problem wurde durch die Einführung von Funktionsprototypen mit folgender Deklarationssyntax behoben:

<type> func(Parameter-Deklaratorliste);

Hinweis:	Sie können den Compiler in der IDE oder in der Kommandozeilen-Version veranlassen, gegebenenfalls die Warnung: "Function called without a prototype"  auszugeben.

Deklaratoren legen den Typ für jeden Funktionsparameter fest. Der Compiler verwendet diese Information, um den Funktionsaufruf auf seine Gültigkeit hin zu überprüfen. Der Compiler kann dann auch Argumente in den benötigten Typ konvertieren. Nehmen wir an, Sie haben folgendes Programmfragment geschrieben:

extern long lmax(long v1, long v2); /* Prototyp */

foo()

{

   int limit = 32;

   char ch = 'A';

   long mval;

   mval = lmax(limit,ch);    /* Funktionsaufruf */

}

Da das Programm für lmax einen Funktionsprototyp hat, werden limit und ch – gemäß den gebräuchlichen Konvertierungsregeln – in den Typ long konvertiert, bevor sie für den Aufruf von lmax auf dem Stack abgelegt werden. Ohne Funktionsprototyp würden limit und ch als Integer und Zeichen auf den Stack gelegt. In diesem Fall würden die vom Stack an lmax übergebenen Werte in Größe und Inhalt nicht der von lmax erwarteten Übergabe entsprechen – und dies würde zu Problemen führen. Die klassische Deklaration läßt keine Überprüfung des Parametertyps und der Parameteranzahl zu. Deshalb ist der Einsatz von Funktionsprototypen eine große Hilfe bei der Fehlersuche.

Funktionsprototypen unterstützen auch die Programmdokumentation. Die Funktion strcpy zum Beispiel benötigt zwei Argumente: einen Quellstring und einen Zielstring. Die Frage ist, welcher ist welcher? Der Funktionsprototyp

char *strcpy(char *dest, const char *source);

beantwortet diese Frage. Befinden sich die Funktionsprototypen in einer Header-Datei, dann können Sie sich diese Include-Datei ausdrucken lassen. Damit steht Ihnen der größte Teil der Informationen zur Verfügung, die Sie in Ihrem Programm für den Aufruf dieser Funktionen benötigen. Wenn ein Prototyp-Parameter einen Bezeichner enthält, wird dieser nur für spätere Fehlermeldungen benutzt, die diesen Parameter betreffen; ansonsten wird er nicht verwendet.

Ein Funktionsdeklarator, der nur das Wort void in Klammern umfaßt, repräsentiert eine Funktion, die keine Argumente benötigt:

func(void);

In C++ reicht hierzu die Deklaration func() aus.

Ein Funktionsprototyp deklariert normalerweise eine Funktion mit einer festen Anzahl von Parametern. Bei Funktionen, die eine unterschiedliche Anzahl von Parametern haben können (wie zum Beispiel printf), endet der Funktionsprototyp mit einem Auslassungszeichen (...), zum Beispiel:

f(int *count, long total, ...)

Bei dieser Art Prototyp werden die festen Parameter während des Kompilierens überprüft und die variablen Parameter ohne Typ-Prüfung weitergegeben.

Hinweis:	stdarg.h und varargs.h enthalten Makros, die Sie in selbstdefinierten Funktionen mit variabler Parameteranzahl verwenden können.

Im folgenden finden Sie einige weitere Beispiele für Funktionsdeklaratoren und Prototypen:

int  f();	/* In C liefert diese Funktion einen int
Es sind keine Informationen über Parameter enthalten

Dies ist klassischer K&R-Stil */

int f();	/* In C++ eine Funktion ohne Argumente */

int  f(void);	* Eine Funktion ohne Parameter, die int liefert. */

int  p(int,long);	/* Eine Funktion, die int liefert und zwei Parameter
erwartet: einen int und einen long. */

int  _ _pascal q(void);	/* Eine Pascal-Funktion ohne Parameter, die einen int
liefert. */

char _ _far *s(char *source, int kind);	/* Eine Funktion, die einen far­Zeiger	auf einen Char liefert und zwei Parameter	erwartet: einen Zeiger auf einen Char und	einen int. */

int  printf(char *format,...;  /*	Eine Funktion, die einen int liefert und
einen festen Char-Parameter und zusätzliche
Argumente unbekannten Typs erwartet. */

int  (*fp)(int)	/* Ein Zeiger auf eine Funktion, die einen int liefert
und einen einzelnen int-Parameter erwartet. */


Definitionen

Siehe auch

Externe Funktionsdefinitionen erläutert die allgemeine Syntax für externe Funktionsdefinitionen.

Externe Funktionsdefinitionen

Datei

Extern-Definition

Datei  Extern-Definition

Extern-Definition:

Funktionsdefinition

Deklaration

asm-Anweisung

Funktions-Definition:

<Mehrere-Deklarations-Spezifizierer> Deklarator <Deklarationsliste>

Blockanweisung

Eine Funktionsdefinition hat im allgemeinen die im folgenden aufgelisteten Bestandteile, wobei die Grammatik für komplizierte Fälle weitere Komponenten zuläßt:

1.	Optionale Speicherklassen-Bezeichner: extern oder static. Voreingestellt ist extern.

2.	Ein Rückgabewert, auch void möglich; voreingestellt ist int.

3.	Optionale Modifizierer: _ _pascal, _ _cdecl, _ _export, _ _interrupt, _ _near, _ _far, _ _huge, _ _loadds, _ _saveregs. Die Voreinstellung hängt vom Speichermodell und den Compilereinstellungen ab.

4.	Der Name der Funktion.

5.	Eine Parameter-Deklarationsliste (auch eine leere Liste möglich), die in runden Klammern steht. Wenn Sie in C eine leere Liste angeben möchten, sollten Sie func(void) schreiben. Die traditionelle Schreibweise func() ist in C zulässig, aber veraltet und möglicherweise unsicher.

6.	Ein Funktionsrumpf, der den auszuführenden Programm-Quelltext für einen Funktionsaufruf repräsentiert.

Hinweis:	Die Elemente von 1. und 2. können kombiniert werden.


Deklaration formaler Parameter

Siehe auch

Die Syntax der Deklarationsliste für formale Parameter ist derjenigen der Deklaratoren in normalen Bezeichnerdeklarationen ähnlich. Im folgenden finden Sie einige Beispiele:

int func(void) {                   // keine Argumente

int func(T1 t1, T2 t2, T3 t3=1) { // drei einfache Parameter, der letzte mit

                                    // Vorgabewert

int func(T1* ptr1, T2& tref) {     // ein Zeiger und eine Referenz

int func(register int i) {         // fordert ein Register für das Argument an

int func(char *str,...) {          /* ein String und eine variable Anzahl weiterer

                               Argumente oder eine feste Anzahl von Argumenten mit                              variablen Typen */

In C++ können Sie einem formalen Parameter einen Vorgabewert zuordnen. Parameter mit voreingestellten Werten müssen die letzten Argumente in der Parameterliste sein. Die Argumente können vom Typ Skalar, Struktur, Variante oder Aufzählung sein. Es kann sich auch um Zeiger oder Referenzen auf Strukturen und Varianten oder um Zeiger auf Funktionen oder Klassen handeln.

Das Auslassungszeichen (...) zeigt an, daß die Funktion bei verschiedenen Gelegenheiten mit unterschiedlich vielen Argumenten aufgerufen werden kann. Vor dem Auslassungszeichen kann eine Liste mit bekannten Argumentdeklarationen stehen. Diese Form des Prototyps verringert den Umfang der Überprüfung, die der Compiler durchführen kann.

Die deklarierten Parameter haben während der Funktionsdauer einen automatischen Gültigkeitsbereich und automatische Dauer. Der einzige zulässige Speicherklassen-Bezeichner ist register.

Die Modifizierer const und volatile können mit formalen Parameterdeklaratoren verwendet werden.


Funktionsaufrufe und Parameterumwandlungen

Siehe auch

Beim Aufruf einer Funktion werden die aktuellen Argumente in der Reihenfolge ihrer formalen Parameter aufgeführt. Die aktuellen Argumente werden – wie bei der Initialisierung – in die Typen der formalen Parameter konvertiert.

Im folgenden sind die Regeln zusammengefaßt, die festlegen, wie C++Builder mit und ohne Prototypen die Sprachmodifizierer und formalen Parameter in Funktionsaufrufen behandelt:

Die Sprachmodifizierer für eine Funktionsdefinition müssen mit den Modifizierern in der Funktionsdeklaration bei allen Aufrufen der Funktion übereinstimmen.

Eine Funktion kann die Werte ihrer formalen Parameter ändern. Dies hat aber keine Auswirkung auf die Argumente in der aufrufenden Routine. Die Ausnahme bilden Referenzargumente in C++.

Wurde kein Funktionsprototyp deklariert, konvertiert C++Builder Integerargumente in einem Funktionsaufruf entsprechend den Integer-Erweiterungsregeln, die im Abschnitt arithmetische Standard-Umwandlungen beschrieben sind. Steht im Gültigkeitsbereich ein Prototyp, dann konvertiert C++Builder das gegebene Argument in den Typ des deklarierten Parameters, genauso wie bei einer Zuweisung.

Enthält ein Funktionsprototyp ein Auslassungszeichen (...), dann konvertiert C++Builder alle vorhandenen Funktionsargumente – bis hin zum Auslassungszeichen – genauso wie bei jedem anderen Prototyp. Der Compiler konvertiert alle weiteren Argumente entsprechend den normalen Regeln für Funktionsargumente ohne Prototypen.

Ist ein Prototyp vorhanden, muß die Anzahl der Argumente mit der Anzahl der Parameter übereinstimmen (außer, es ist ein Auslassungszeichen vorhanden). Die Typen müssen nur soweit kompatibel sein, daß sie – bei einer Zuweisung – konvertiert werden können. Sie können immer eine explizite Typumwandlung (cast) zur Konvertierung eines Arguments in einen für den Funktionsprototyp zulässigen Typ verwenden.

Hinweis:	Stimmt der Funktionsprototyp nicht mit der Funktionsdefinition überein, findet C++Builder dies nur dann heraus, wenn die Definition in derselben Übersetzungseinheit steht wie der Prototyp. Wenn Sie eine Bibliothek für Routinen mit einer entsprechenden Prototyp-Header-Datei anlegen, dann müssen Sie darauf achten, daß beim Kompilieren der Bibliothek die Include-Datei mit einbezogen wird, damit jegliche Diskrepanzen zwischen den Prototypen  und den Definitionen entdeckt werden. C++ verfügt über eine typensichere Bindung, so daß Unterschiede zwischen erwarteten und tatsächlichen Parametern durch den Linker ausgeschlossen werden.


Strukturen

Siehe auch

Eine Struktur ist ein abgeleiteter Typ, der im Normalfall für eine vom Benutzer definierte Ansammlung von benannten Elementen (oder Komponenten) steht. Die Elemente können mit wenigen Einschränkungen, die später erläutert werden, jeden standardmäßigen oder abgeleiteten Typ annehmen und in beliebiger Reihenfolge aufgeführt werden. Außerdem kann in einer Struktur auch ein Bitfeld verwendet werden, was sonst nicht zulässig ist. Mit den C++Builder Struktur-Typklassen lassen sich komplexe Datenstrukturen fast genauso leicht handhaben wie einzelne Variablen. Die Initialisierung einer Struktur wird im Thema Arrays, Strukturen und Varianten besprochen.

In C++ wird ein Strukturtyp wie ein Klassentyp behandelt. Allerdings gibt es gewisse Unterschiede: Der voreingestellte Zugriff und die Voreinstellung für die Basisklasse sind beide public. Die C++ Zugriff-Spezifizierer public (Voreinstellung), private und protected erlauben eine bessere Kontrolle des Zugriffs auf Strukturelemente. Abgesehen von dieser optionalen Zugriffskontrolle und den erwähnten Ausnahmen gilt die folgende Beschreibung der Struktursyntax und ­benutzung für C und C++ gleichermaßen.

Strukturen werden durch das Schlüsselwort struct definiert. Zum Beispiel:

struct mystruct { ... }; // Mystruct ist der "Tag"-Name der Struktur

    .

    .

struct mystruct s, *ps, arrs[10];

/* s ist vom Typ struct mystruct; ps ist ein Zeiger auf struct mystruct;

   arrs ist ein Array aus struct mystruct. */


Namenlose Strukturen und Typdefinitionen

Siehe auch

Wenn man den Tag-Namen einer Struktur wegläßt, erhält man eine unbenannte Struktur. Sie können unbenannte Strukturen benutzen, um die durch Komma voneinander getrennten Bezeichner in der struct-Bezeichnerliste mit dem angegebenen (oder von ihm abgeleiteten) Strukturtyp zu deklarieren. Zusätzliche Objekte dieses Typs können Sie nirgendwo sonst deklarieren:

struct { ... } s, *ps, arrs[10]; // Struktur ohne "Tag"

Beim Deklarieren einer Struktur können Sie ein typedef mit oder ohne
Tag-Namen erzeugen:

typedef struct mystruct { ... } MYSTRUCT;

MYSTRUCT s, *ps, arrs[10];       // entspricht struct mystruct s, etc.

typedef struct { ... } YRSTRUCT; // kein "Tag"

YRSTRUCT y, *yp, array[20];

Normalerweise verwenden Sie entweder einen Tag-Namen oder ein typedef mit Strukturnamen. Beide können in Strukturdeklarationen verwendet werden.

Unbenannte Struktur- und Variantenelemente werden bei der Initialisierung ignoriert.


Strukturelement-Deklarationen

Siehe auch

Die Element-Deklarationsliste innerhalb der Strukturklammern deklariert die Typen und Namen der Strukturelemente über die Deklaratorsyntax, wie sie in C++Builder Syntax für Deklarationen dargestellt ist.

Ein Strukturelement kann jeden Typ bis auf zwei Ausnahmen annehmen:

Der Typ eines Elements darf nicht mit dem Typ der Struktur identisch sein, die gerade deklariert wird:

struct mystruct { mystruct s } s1, s2; // nicht zulässig

Ein Element kann jedoch einen Zeiger auf die deklarierte Struktur darstellen, wie im folgenden Beispiel:

struct mystruct { mystruct *ps } s1, s2; // zulässig

Ebenso kann eine Struktur schon definierte andere Strukturen enthalten, wenn eine Instanz der deklarierten Struktur deklariert wird.

Außer in C++, kann ein Element nicht den Typ "Funktion, die ... liefert" annehmen, sondern nur den Typ "Zeiger auf Funktion, die ... liefert". In C++ kann eine Struktur Elementfunktionen enthalten.

Hinweis:	Das Schlüsselwort struct kann in C++ weggelassen werden.


Strukturen und Funktionen

Siehe auch

Eine Funktion kann sowohl eine Struktur als auch einen Zeiger auf eine Struktur zurückgeben:

mystruct func1(void);  // func1() liefert eine Struktur

mystruct *func2(void); // func2() liefert einen Zeiger auf eine Struktur

Eine Struktur kann folgendermaßen als Argument an eine Funktion übergeben werden:

void func1(mystruct s);        // direkt

void func2(mystruct *sptr);    // über einen Zeiger

void func3(mystruct &sref);    // als Referenz (nur in C++)


Zugriff auf Strukturelemente

Siehe auch

Auf Struktur- und Variantenelemente kann über die folgenden beiden Auswahloperatoren zugegriffen werden:

. (Punkt)

-> (Pfeil nach rechts)

Ist beispielsweise das Objekt s vom Strukturtyp S, und sptr ein Zeiger auf S. Wenn m nun ein Element-Bezeichner vom Typ M ist, der in S deklariert ist, dann sind die Ausdrücke s.m und sptr->m vom Typ M. Beide stellen das Elementobjekt m in S dar. Der Ausdruck sptr->m steht für (*sptr).m.

Der Operator . ist der direkte und der Operator -> ist der indirekte (oder Zeiger-) Zugang zu Strukturelementen. Sehen Sie folgendes Beispiel:

struct mystruct

{

   int i;

   char str[21];

   double d;

} s, *sptr = &s;

    .

    .

    .

s.i = 3;            // dem Element i von mystruct s einen Wert zuweisen

sptr -> d = 1.23;   // dem Element d von mystruct s einen Wert zuweisen

Der Ausdruck s.m ist unter der Bedingung ein L-Wert, daß s ein L-Wert und m kein Array ist. Der Ausdruck sptr->m ist nur dann ein L-Wert, wenn m kein Array ist.

Wenn die Struktur B ein Feld enthält, das vom Typ Struktur A ist, dann gibt es zwei verschiedene Möglichkeiten, auf die Elemente von A zuzugreifen:

struct A {

   int j;

   double x;

};

struct B {

   int i;

   struct A a;

   double d;

} s, *sptr;

    .

    .

    .

s.i = 3;            // dem Element i von B einen Wert zuweisen

s.a.j = 2;          // dem Element j von A einen Wert zuweisen

sptr->d = 1.23;     // dem Element d von B einen Wert zuweisen

(sptr->a).x = 3.14  // dem Element x von A einen Wert zuweisen

Jede Strukturdeklaration führt einen eigenen Strukturtyp ein. So sind durch die Definitionen

struct A {

   int i,j;

   double d;

} a, a1;

struct B {

   int i,j;

   double d;

} b;

beide Objekte a und a1 Strukturen vom Typ A, aber die Typen der Objekte a und b sind verschieden. Strukturen können einander nur dann zugewiesen werden, wenn Quelle und Ziel denselben Typ haben:

a = a1;   // zulässig: gleicher Typ, daher können die einzelnen Elemente einander zugewiesen werden

a = b;    // nicht zulässig: verschiedene Typen

a.i = b.i; a.j = b.j; a.d = b.d /* aber Sie können die Elemente einzeln zuweisen */


Ausrichtung auf Wortgrenzen in Strukturen

Siehe auch

Speicher wird einer Struktur Element für Element, von links nach rechts, von den niedrigen Speicheradressen zu den höheren hin zugewiesen. Im folgenden Beispiel

struct mystruct {

   int i;

   char str[21];

   double d;

} s;

belegt das Objekt s genügend Speicher für 4-Byte-Integer in einem 32-Bit-Program, für einen 21-Byte-String und einen 8-Byte-double. Die Anordnung dieses Objekts im Speicher wird durch die Option 'Wortausrichtung' bestimmt. Ist sie deaktiviert, dann belegt s lückenlos 33 Bytes beim 32-Bit-Compiler.

Wortausrichtung ist standardmäßig deaktiviert. Wird die Option Wortausrichtung eingeschaltet, dann füllt C++Builder die Struktur so mit einzelnen Bytes auf, daß:

32-Bit Compiler-Ausrichtung

1 	die Strukturgrenzen werden durch Vielfache von 4 Bytes festgelegt.

2 	jedes Element, das nicht vom Typ char ist, liegt an einem Offset, der ein Vielfaches der Elementgröße ist. Ein short liegt an einem Offset, der ein Vielfaches von 2 ints vom Beginn der Struktur an ist.

3	an das Ende der Struktur können bei Bedarf ein bis drei Bytes angehängt werden, damit die Größe der Struktur ein Vielfaches von 4 Bytes ist.

Der 32-Bit-Compiler würde mit aktiver Wortausrichtung vor dem double drei Bytes in die Struktur einfügen, wodurch ein 36-Byte-Objekt entstehen würde.


Namensbereich von Strukturen

Siehe auch

Tag-Namen von Strukturen, Varianten und Aufzählungstypen teilen sich denselben Namensbereich (nur Aufzählungstypen innerhalb von Strukturen haben in C++ einen anderen Namensbereich). Das bedeutet, daß derartige Tags innerhalb eines Gültigkeitsbereichs eindeutig sein müssen. Sie müssen sich aber nicht von Bezeichnern in den anderen drei Namensbereichen unterscheiden: dem Label-, Element- und Einzel-Namensbereich (bestehend aus Variablen, Funktionen, typedef-Namen und Aufzählungstypen).

Elementnamen innerhalb einer Struktur oder Variante müssen eindeutig sein, können sich aber in anderen Strukturen oder Varianten wiederholen. Zum Beispiel:

goto s;

    .

    .

    .

s:           // Label

struct s {   // OK: Tag und Label haben unterschiedliche Namensbereiche

   int s;    // OK: Tag, Label und Element haben unterschiedliche Namensbereiche

   float s;  // UNGÜLTIG: doppelter Elementname

} s;         // OK: Variablen besitzen eigenen Namensbereich. In C++ ist dies nur

             // möglich , wenn s keinen Konstruktor hat.

union s {    // UNGÜLTIG: Tag im gleichen Namensbereich

   int s;    // OK: Neuer Namensbereich für Elemente

   float f;

} f;         // OK: Variablen besitzen eigenen Namensbereich.

struct t {

   int s;    // OK: anderer Namensbereich für Elemente

    .

    .

    .

} s;         // UNGÜLTIG: doppelter Variablenname


Unvollständige Deklarationen

Siehe auch

Unvollständige Deklarationen werden auch als forward-Deklarationen berzeichnet.

Ein Zeiger auf eine Struktur vom Typ A kann in der Deklaration einer anderen Struktur B bereits verwendet werden, bevor die Struktur A deklariert ist:

struct A;                  // unvollständig

struct B { struct A *pa };

struct A { struct B *pb };

Das erste Auftreten von A ist unvollständig, weil A an diesem Punkt noch nicht definiert wird. Unvollständige Deklarationen sind in diesem Zusammenhang aber erlaubt, weil für die Definition von B die Größe von A nicht benötigt wird.


Bitfelder

Siehe auch

Sie können signed und unsigned Integerelemente als Bitfelder mit einer Größe von 1 bis 16 bzw. von 1 bis 32 definieren, je nachdem, welche Plattform Sie für Ihre Anwendung wählen. Sie geben die Größe des Bitfeldes und den optionalen Bezeichner wie folgt an:

Typ-Bezeichner<Bitfeld-Bezeichner> : Größe;

Als Typ-Bezeichner sind char, unsigned char, int oder unsigned int erlaubt. Bitfelder werden innerhalb eines Wortes vom niederwertigen zum höherwertigen Bit hin angeordnet. Die Größe eines Bitfeldes muß angegeben werden und einen konstanten Integerwert zwischen 1 und 16 bzw. zwischen 1 und 32 haben, in Abhängigkeit von der Zielplattform (16- bzw. 32-Bit).

Wenn man den Bezeichner des Bitfeldes wegläßt, wird die angegebene Größe des Bitfeldes angelegt, aber das Feld ist nicht verwendbar. Damit können Bitmuster wie z.B. Hardware-Register nachgebildet werden, in denen einige Bits ungenutzt sind. Zum Beispiel ergibt

struct mystruct

  int       i : 2;

  unsigned  j : 5;

  int         : 4;

  int       k : 1;

  unsigned  m : 4;

) a, b, c;

das folgende Bild:

Integerfelder werden in Zweier-Komplementform angelegt, wobei das linke Bit das höchstwertige (MSB = most significant bit) ist. Bei Bitfeldern, die als int (zum Beispiel als signed int) deklariert sind, wird das MSB als Vorzeichenbit interpretiert. Ein Bitfeld der Größe 2, in dem der Binärwert 11 gespeichert ist, würde deswegen als 3 interpretiert, wenn es als unsigned deklariert ist und als -1, wenn es als int deklariert ist. Im vorhergehenden Beispiel würde die erlaubte Zuweisung a.i = 6 den Binärwert 10, der dezimal -2 bedeutet, ohne Warnung in a.i stehenlassen. Das als signed int deklarierte Bitfeld k mit der Größe 1 kann dann natürlich nur die Werte -1 und 0 annehmen, weil der Bitwert 1 als -1 interpretiert wird.

Bitfelder können nur in Strukturen, Varianten und Klassen verwendet werden. Auf Bitfelder wird genauso zugegriffen wie auf alle anderen Elemente ( . und ->). Allerdings erzeugen Bitfelder einige Probleme, wenn portierbarer Code geschrieben werden soll, da die Organisation von Bits innerhalb von Bytes und von Bytes innerhalb von Worten maschinenabhängig ist.

Der Ausdruck &mystruct.x ist nicht zulässig, wenn x ein Bitfeld-Bezeichner ist, da es keine Garantie dafür gibt, daß mystruct.x auf einer Byte-Adresse liegt.


Varianten

Siehe auch

Die Variante (union)ist ein abgeleiteter Typ, der in vielen syntaktischen und funktionalen Eigenschaften mit dem Strukturtyp übereinstimmt. Der hauptsächliche Unterschied zur Struktur ist, daß in der Variante zu einem gegebenen Zeitpunkt nur eines der Elemente "aktiv" sein kann. Die Größe einer Variante wird von der Größe ihres größten Elements bestimmt. Es kann jeweils nur der Wert eines der Elemente gespeichert werden. Im folgenden Beispiel

union myunion {     /* Varianten-Tag = myunion */

   int i;

   double d;

   char ch;

} mu, *muptr=&mu;

kann der Bezeichner mu, der vom Typ union myunion ist, dazu benutzt werden, einen 2-Byte-Integer, einen 8-Byte- double oder einen Einzel-Byte-char zu speichern (aber nur jeweils einen dieser drei Typen).

Hinweis:	Varianten entsprechen dem varianten Recordtyp von Pascal und Modula-2.

sizeof(union myunion) und sizeof(mu) geben beide 8 zurück. Allerdings sind 6 Bytes davon ungenutzt, wenn mu verwendet wird, um ein Integer-Objekt zu speichern bzw. 7 Bytes, wenn mu verwendet wird, um einen char zu speichern. Auf die Elemente einer Variante wird genauso zugegriffen wie auf die Elemente einer Struktur (. und ->). Aber es ist Vorsicht geboten:

mu.d = 4.016;

printf("mu.d = %f\n",mu.d);   //OK: zeigt an mu.d = 4.016
printf("mu.i = %d\n",mu.i); //unbrauchbares Ergebnis
mu.ch = 'A';

printf("mu.ch = %c\n",mu.ch); //OK: zeigt an mu.ch = A
printf("mu.d = %f\n",mu.d); //unbrauchbares Ergebnis
muptr->i = 3;

printf("mu.i = %d\n",mu.i); //OK: zeigt an mu.i = 3

In diesem Beispiel ist das zweite printf zwar erlaubt, weil mu.i wirklich ein Integertyp ist. Allerdings entspricht das Bitmuster in mu.i einem Teil des vorher zugewiesenen double und dürfte wohl selten ein brauchbares Resultat ergeben.

Bei einer korrekten Konvertierung zeigt ein auf eine Variante gerichteter Zeiger auf jedes Element dieser Variante und umgekehrt.


Anonyme Varianten (nur in C++)

Siehe auch

Eine Variante, die keinen Namen hat und nicht zur Deklaration eines benannten Objekts (oder anderen Typs) verwendet wird, heißt anonyme Variante und besitzt folgende Syntax:

union { Element-Liste };

Auf ihre Elemente kann in dem Gültigkeitsbereich, in dem die Variante deklariert ist, direkt – ohne die x.y oder p->y-Syntax – zugegriffen werden.

Anonyme Varianten können keine Elementfunktionen enthalten und müssen auf Dateiebene als statisch deklariert sein. Mit anderen Worten: eine anonyme Variante kann keine externe Bindung haben.


Deklaration von Varianten

Siehe auch

Die allgemeine Deklarationssyntax für Varianten stimmt fast vollständig mit der für Strukturen überein. Es gibt jedoch folgende Unterschiede:

Varianten können Bitfelder enthalten, aber nur jeweils eines der Bitfelder kann aktiv sein. Alle diese Bitfelder beginnen am Anfang der Variante. (Beachten Sie, daß Bitfelder maschinenabhängig sind und es dadurch beim Schreiben von portierbarem Programm-Quelltext zu Problemen kommen kann.)

Anders als C++ Strukturen können C++ Varianten nicht die Klassenzugriffs-Bezeichner public, private und protected verwenden. Alle Felder einer Variante sind öffentlich.

Varianten können nur über ihr erstes deklariertes Element initialisiert werden:

union local87 {

   int i;

   double d;

   } a = { 20 };

Eine Variante kann kein Bestandteil einer Klassenhierarchie sein. Sie kann weder eine Basisklasse sein noch von einer anderen Klasse abgeleitet werden. Eine Variante kann aber einen Konstruktor haben.


Aufzählungen

Siehe auch

Der Datentyp Aufzählung erlaubt es, statt einer Menge von konstanten Integerwerten entsprechende Bezeichner zu verwenden. Zum Beispiel wird in der folgenden Deklaration:

enum tage { so, mo, di, mi, do, fr, sa } alletage;

eine Variable alletage vom Aufzählungstyp enum tage deklariert und dazu eine Menge von Aufzählungselementen (so, mo,...), die für konstante Integerwerte stehen.

C++Builder kann Aufzählungen in einzelnen Bytes speichern, wenn die Option Enum als Integer behandeln (O|C|Codeerzeugung) deaktiviert ist oder der Schalter -b verwendet wird. Die Standardeinstellung ist <an> (Aufzählungstypen sind also normalerweise vom Typ int), wenn der Wertebereich es zuläßt. In Ausdrücken werden die Werte von Aufzählungstypen immer auf Integer ausgedehnt. Die Bezeichner in einer Aufzählungsliste sind implizit vom Typ signed char, unsigned char oder int, abhängig vom Wertebereich der Aufzählung. Können alle Werte in einem signed oder unsigned char gespeichert werden, wird einer dieser Typen verwendet.

In C kann einer Aufzählungstypvariablen jeder Wert vom Typ int zugewiesen werden – es gibt keine darüberhinausgehende Typenprüfung. In C++ dagegen kann einer derartigen Variable nur einer der bereits aufgezählten Bezeichner zugewiesen werden, zum Beispiel:

alletage = mo;       // OK

alletage = 1;         // ungültig, obwohl mo == 1

Der Bezeichner tage ist ein optionales Aufzählungs-Tag und kann in nachfolgenden Deklarationen von Aufzählungsvariablen des Typs enum tage verwendet werden:

enum tage Zahltag, Feiertag; // deklariert zwei Variablen

In C++ können Sie das Schlüsselwort enum auch weglassen, wenn tage im aktuellen Gültigkeitsbereich keine andere Bedeutung hat.

Genauso wie bei Strukturen und Varianten kann man den Tag-Namen weglassen, wenn keine weiteren Variablen dieses Aufzählungstyps benötigt werden:

enum { so, mo, di, mi, do, fr, sa } alletage;

/* anonymer Aufzählungstyp */

Die Aufzählungsbezeichner innerhalb der geschweiften Klammern heißen Aufzählungskonstanten. Jeder dieser Konstanten ist ein fester Integerwert zugewiesen. Sind keine expliziten Initialisierer vorhanden, wird die erste Konstante (so) auf Null gesetzt und die folgenden in Einerschritten aufsteigend belegt (mo = 1, di = 2 usw.). Weitere Informationen hierzu finden Sie unter Aufzählungs-Konstanten.

Mittels expliziter Initialisierer können Sie jedoch eine oder mehrere Konstanten auf einen bestimmten Wert setzen. Alle folgenden Konstanten werden dann mit jeweils um 1 steigenden Konstanten initialisiert. In dem folgenden Deklarationsbeispiel:

/* Die Initialisierung kann bereits deklarierte Aufzählungskonstanten enthalten */

enum coins { penny = 1, tuppence, nickel = penny + 4, dime = 10,

             quarter = nickel * nickel } smallchange;

hat tuppence den Wert 2, nickel den Wert 5 und quarter den Wert 25.

Zur Initialisierung kann jeder Ausdruck verwendet werden, der einen positiven oder negativen Integerwert ergibt (nach möglichen Integerausdehnungen). Diese Werte sind normalerweise eindeutig, doppelte Werte sind jedoch erlaubt.

Aufzählungstypen können immer dann verwendet werden, wenn int-Typen zulässig sind.

enum tage { so, mo, di, mi, do, fr, sa } alletage;

enum tage Zahltag;

typedef enum tage TAGE;

TAGE *tageptr;

int i = di;

alletage = mo;         // OK

*tageptr = alletage;  // OK

mo = di;              // UNGÜLTIG: mo ist eine Konstante

Die Tag-Namen von Aufzählungstypen teilen sich den Namensbereich mit Struktur und Varianten. Die aufgezählten Konstanten teilen sich den Namensbereich mit normalen Variablen:

int mo = 11;

{

   enum tage { so, mo, di, mi, do, fr, sa } alletage;

   /* Die Aufzählungskonstante mo verdeckt die vorherige Deklaration von int mo */

   struct tage { int i, j;};   // UNGÜLTIG: tage ist doppelter Tag

   double sa;                 // UNGÜLTIG: Neudefinition von sa

}

mo = 12;                      // zurück im Gültigkeitsbereich von int mo

In C++ gelten Aufzählungskonstanten, die in einer Klasse definiert wurden, nur im Gültigkeitsbereich dieser Klasse.

In C++ können die meisten Operatoren für einen Aufzählungstyp überladen werden. Weil die Operatoren =, [ ], () und ­> jedoch als Elementfunktion überladen sein müssen, können sie nicht für eine Aufzählung überladen werden. Im folgenden Beispiel für das Überladen von Operatoren des Aufzählungstyps enum wird gezeigt, wie die Operatoren Suffix- und Präfix-Inkrement überladen werden.


Überladen von Operatoren des Aufzählungstyps enum

   // ÜBERLADEN DER SUFFIX- UND PRÄFIX-INKREMENT-OPERATOREN FÜR enum

   #include <iostream.h>

   enum _JAHRESZEIT { Frühling, Sommer, Herbst, Winter };

   _JAHRESZEIT operator++(_JAHRESZEIT &s) {      // PRÄFIX INKREMENT

      _JAHRESZEIT tmp = s; //  ORIGINALWERT SPEICHERN

      /* MODULARE BERECHNUNG DURCHFÜHREN UND
       DAS ERGEBNIS IN DEN TYP _JAHRESZEIT UMWANDELN */

      s = _JAHRESZEIT( (s + 1) % 4 );        //  ORIGINAL INKREMENTIEREN

      return s;                          // ALTEN WERT ZURÜCKGEBEN

      }

   // DAS UNBENANNTE int ARGUMENT WIRD NICHT VERWENDET

   _JAHRESZEIT operator++(_JAHRESZEIT &s, int) { // SUFFIX-INKREMENT

      _JAHRESZEIT tmp = s;

      switch (s) {

         case Frühling: s = Sommer; break;

         case Sommer: s = Herbst; break;

         case Herbst:   s = Winter; break;

         case Winter: s = Frühling; break;

         }

      return (tmp);

      }

   int main(void) {

      _JAHRESZEIT Jahreszeit = Herbst;

      cout << "\nJahreszeit " << Jahreszeit;

      cout << "\nJahreszeit inkrementieren: "<< ++Jahreszeit;

      cout << "\nKeine Veränderung bei Postfix: " << Jahreszeit++;

      cout << "\nJahreszeit nach Postfix:" << Jahreszeit;

      return 0;

      }

Dieses Programm erzeugt folgende Ausgabe:

Jahreszeit 2

Jahreszeit inkrementieren: 3

Keine Veränderung bei Postfix: 3

Jahreszeit nach Postfix:0


Zuweisung an den Typ enum

Die Regeln für Ausdrücke, die enum-Typen enthalten, sind strenger geworden. Der Compiler unterstützt diese Regeln durch Fehlermeldungen, wenn der Compiler-Schalter -A eingeschaltet ist (d.h. ANSI C++ Konformität).

Der Versuch, einer Variable vom Typ enum ein Integer zuzuweisen, verursacht einen Fehler:

enum color

{

  red, green, blue

};

int f()

{

  color c;

  c = 0;

  return c;

}

Das selbe geschieht, wenn ein Integer als Parameter an eine Funktion überwiesen wird. Beachten Sie, daß das Ergebnis des Ausdrucks flag1|flag2 vom Typ int ist.

enum e

{

  flag1 = 0x01,

  flag2 = 0x02

};

void p(e);

void f()

{

  p(flag1|flag2);

}

Um das Beispiel kompilieren zu können, muß der Ausdruck flag1|flag2 in den enum-Typ: e (flag1|flag2) umgewandelt werden.


Ausdrücke

Siehe auch

Ein Ausdruck ist eine Folge von Operatoren, Operanden und Interpunktionszeichen, die eine Berechnung definiert. Die in C++Builder Ausdrücke dargestellte formale Syntax zeigt, daß Ausdrücke rekursiv definiert sind. Für die Schachtelungstiefe gibt es keine formale Begrenzung. (Allerdings wird der Compiler die Fehlermeldung "Out-of-Memory" bringen, wenn er einen zu komplexen Ausdruck übersetzen soll.)

Hinweis:	Wie Bezeichner und Operatoren zu grammatikalisch korrekten Ausdrücken kombiniert werden können, finden Sie in C++Builder Ausdrücke.

Die Auswertung von Ausdrücken richtet sich nach bestimmten Konvertierungs- und Gruppierungsregeln, nach der Abarbeitungsreihenfolge und der Richtung der Operatoren, dem Vorhandensein von Klammern und den Datentypen der Operanden. Die hierfür nötigen Standardumwandlungen sind detailliert im Thema Methoden für arithmetische Standardumwandlungen beschrieben. Die Reihenfolge, in der Sie Operanden und Unterausdrücke anordnen, muß nicht notwendigerweise die Reihenfolge sein, in der C++Builder Ihre Ausdrücke auswertet (siehe Auswertungsreihenfolge).

Ausdrücke können einen L-Wert, einen R-Wert oder keinen Wert ergeben. Ausdrücke können zu Nebeneffekten führen, gleichgültig, ob sie einen Wert ergeben oder nicht.

Eine Zusammenfassung der Rangfolge und Abarbeitungsreihenfolge finden Sie im Thema Abarbeitungsreihenfolge und Rangfolge der C++Builder Operatoren. Die komplette Definition der Abarbeitungsreihenfolge und -richtung (Assoziativität) der Operatoren finden Sie in C++Builder Ausdrücke.

C++Builder Ausdrücke

Primärer Ausdruck:

Literal

this (C++ spezifisch)

:: Bezeichner (C++ spezifisch)

:: Operator-Funktionsname (C++ spezifisch)

:: Qualifizierter Name (C++ spezifisch)

(Ausdruck)

Name

Literal:

Integer-Konstante

Zeichen-Konstante

Gleitkomma-Konstante

String-Konstante

Name:

Bezeichner

Operator-Funktionsname (C++ spezifisch)

Konvertierungs-Funktionsname (C++ spezifisch)

~ Klassenname (C++ spezifisch)

Qualifizierter Name (C++ spezifisch)

Qualifizierter Name: (C++ spezifisch)

Qualifizierter Klassenname :: Name

Suffix-Ausdruck:

Primärer-Ausdruck

Suffix-Ausdruck  [ Ausdruck ]

Suffix-Ausdruck  ( <Ausdrucksliste> )

Einfacher-Typname    ( <Ausdrucksliste> ) (C++ spezifisch)

Suffix-Ausdruck  .  Name

Suffix-Ausdruck  ->  Name

Suffix-Ausdruck  ++

Suffix-Ausdruck  - -

const_cast < Typ-ID > ( Ausdruck ) (C++ spezifisch)

dynamic_cast < Typ-ID > ( Ausdruck ) (C++ spezifisch)

reinterpret_cast < Typ-ID > ( Ausdruck ) (C++ spezifisch)

static_cast < Typ-ID > ( Ausdruck ) (C++ spezifisch)

typeid ( Ausdruck ) (C++ spezifisch)

typeid ( Typname ) (C++ spezifisch)

Ausdrucksliste:

Zuweisungs-Ausdruck

Ausdrucksliste  ,  Zuweisungs-Ausdruck

unärer Ausdruck:

Suffix-Ausdruck

++ unärer Ausdruck

- - unärer Ausdruck

unärer Operator cast-Ausdruck

sizeof unärer Ausdruck

sizeof ( Typname )

Speicherreservierungs-Ausdruck (C++ spezifisch)

Speicherfreigabe-Ausdruck (C++ spezifisch)

unärer Operator: einer von & * + - !

Speicherreservierungs-Ausdruck: (C++ spezifisch)

<::> new <Plazierung> eingeschränkter Typname <Initialisierer>

<::> new <Plazierung> (Typname) <Initialisierer>

Plazierung: (C++ spezifisch)

( Ausdrucksliste )

eingeschränkter Typname: (C++ spezifisch)

Typ-Spezifizierer <eingeschränkter Deklarator>

eingeschränkter Deklarator: (C++ spezifisch)

ptr-Operator < eingeschränkter Deklarator>

eingeschränkter Deklarator [ <Ausdruck> ]

Speicherfreigabe-Ausdruck: (C++ spezifisch)

<::> delete cast-Ausdruck

<::> delete [ ] cast-Ausdruck

cast-Ausdruck:

unärer Ausdruck

( Typname ) cast-Ausdruck

pm-Ausdruck:

cast-Ausdruck

pm-Ausdruck .* cast-Ausdruck (C++ spezifisch)

pm-Ausdruck ->* cast-Ausdruck (C++ spezifisch)

Multiplikativer Ausdruck:

pm-Ausdruck

Multiplikativer Ausdruck  *  pm-Ausdruck

Multiplikativer Ausdruck  /  pm-Ausdruck

Multiplikativer Ausdruck  %  pm-Ausdruck

Additiver Ausdruck:

Multiplikativer Ausdruck

Additiver Ausdruck  + Multiplikativer Ausdruck

Additiver Ausdruck  - Multiplikativer Ausdruck

shift-Ausdruck:

Additiver Ausdruck

shift-Ausdruck  << Additiver Ausdruck

shift-Ausdruck  >> Additiver Ausdruck

Relationaler Ausdruck:

shift-Ausdruck

Relationaler Ausdruck  <   shift-Ausdruck

Relationaler Ausdruck  >   shift-Ausdruck

Relationaler Ausdruck  <=  shift-Ausdruck

Relationaler Ausdruck  >=  shift-Ausdruck

Gleichheits-Ausdruck:

Relationaler Ausdruck

Gleichheits-Ausdruck  == Relationaler Ausdruck

Gleichheits-Ausdruck  != Relationaler Ausdruck

AND-Ausdruck:

Gleichheits-Ausdruck

AND-Ausdruck  & Gleichheits-Ausdruck

Exklusiv-OR-Ausdruck:

AND-Ausdruck

Exklusiv-OR-Ausdruck ^ AND-Ausdruck

Inklusiv-OR-Ausdruck:

Exklusiv-OR-Ausdruck

Inklusiv-OR-Ausdruck | Exklusiv-OR-Ausdruck

logischer AND-Ausdruck:

Inklusiv OR-Ausdruck

logischer AND-Ausdruck && Inklusiv OR-Ausdruck

logischer OR-Ausdruck:

logischer AND-Ausdruck

logischer OR-Ausdruck || logischer AND-Ausdruck

Bedingter Ausdruck:

logischer OR-Ausdruck

logischer OR-Ausdruck ? Ausdruck : Bedingter Ausdruck

Zuweisungs Ausdruck:

Bedingter Ausdruck

unärer Ausdruck  Zuweisungs-Operator  Zuweisungs- Ausdruck

Zuweisungs-Ooperator: einer von

=	*=	/=	%=	+=	-=

<<	=>	>=	&=	^=	|=

Ausdruck:

Zuweisungs-Ausdruck

Ausdruck , Zuweisungs-Ausdruck

Konstantenausdruck:

Bedingter Ausdruck


Abarbeitungsreihenfolge und Rangfolge von Operatoren

Operatoren

Es gibt 16 Kategorien vorrangige, von denen einige nur einen Operator enthalten. Operatoren in der gleichen Kategorie habe untereinander die gleiche Rangfolge.

Dort, wo doppelte Operatoren in der Tabelle erscheinen, ist das erste Vorkommen unär und das zweite binär. Jede Kategorie verfügt über Verbindungsregeln: Links nach rechts oder rechts nach links. Falls keine runden Klammern vorhanden sind, lösen diese Regeln die Gruppierung der Ausdrücke bei gleichrangigen Operatoren.

Der Vorrang jedes Operators in der folgenden Tabelle ist durch seine Rangfolge in der Tabelle angezeigt. Die erste Kategorie (in der ersten Zeile) hat die höchste Priorität. Operatoren der gleichen Zeile haben die gleiche Priorität.

Operatoren	Abarbeitungsreihenfolge

()	[]	->	::	.							von links nach rechts
!	~	+	-	++	--	&	*	sizeof	new	delete	von rechts nach links
.*	->*										von links nach rechts
*	/	%									von links nach rechts
+	-										von links nach rechts
<<	>>										von links nach rechts
<	<=	>	>=								von links nach rechts
==	!=										von links nach rechts
&											von links nach rechts
^											von links nach rechts
|											von links nach rechts
&&											von links nach rechts
||											von rechts nach links
?:											von links nach rechts
=	*=	/=	%=	+=	-=	&=	^=	|=	<<=	>>=	von rechts nach links
,											von links nach rechts


Ausdrücke und C++

Siehe auch

C++ erlaubt das Überladen von einigen C-Standardoperatoren wie in Thema Überladen von Operator-Funktionen erläutert. Ein überladener Operator verhält sich in spezieller vordefinierter Art und Weise, wenn er in Ausdrücken eines Klassentyps verwendet wird. So könnte zum Beispiel der Gleichheitsoperator == in der Klasse complex so definiert werden, daß er zwei komplexe Zahlen auf Gleichheit überprüft, ohne daß seine normale Verwendbarkeit in Datentypen, die keiner Klasse angehören, berührt wird.

Ein überladener Operator ist als Funktion implementiert; diese Funktion bestimmt den Typ der Operanden, den L-Wert und die Auswertungsreihenfolge, die angewendet werden, wenn der überladene Operator benutzt wird. Das Überladen kann die Abarbeitungsreihenfolge eines Operators jedoch nicht ändern. C++ erlaubt auch benutzerdefinierte Konvertierungen zwischen Klassenobjekten und Grundtypen. Beachten Sie, daß einige der C-Regeln für Operatoren und Konversionen nicht unbedingt für Ausdrücke in C++ zutreffen.


Auswertungsreihenfolge

Siehe auch

Die Reihenfolge, in der C++Builder den Operanden eines Ausdrucks Werte zuweist, ist nicht festgelegt, es sei denn, ein Operator legt eine bestimmte Reihenfolge fest. Der Compiler versucht, den Ausdruck so anzuordnen, daß ein möglichst effizienter Code entsteht. Vorsicht ist daher in solchen Ausdrücken angebracht, in denen ein Wert mehr als einmal modifiziert wird. Generell sollte man die Verwendung von Ausdrücken vermeiden, die Werte eines Objekts sowohl ändern als auch verwenden. Betrachten Sie den folgenden Ausdruck:

i = v[i++];  // i ist undefiniert

Der Wert von i hängt davon ab, ob i vor oder nach der Zuweisung erhöht wird. Genauso ist

int total = 0;

sum = (total = 3) + (++total); // sum = 4 oder sum = 7 ??

für sum und total mehrdeutig. Diese Mehrdeutigkeit kann über eine temporäre Variable aufgelöst werden:

int temp, total = 0;

temp = ++total;

sum = (total = 3) + temp;

Wo die Syntax allerdings eine Auswertungsreihenfolge erzwingt, kann man problemlos mehrere Auswertungen einsetzen:

sum = (i = 3, i++, i++); // OK: sum = 4, i = 5

Jeder Unterausdruck des durch Komma getrennten Ausdrucks in Klammern wird von links nach rechts ausgewertet und der gesamte Ausdruck nimmt den Wert ganz rechts an.

C++Builder gruppiert Ausdrücke um, indem assoziative und kommutative Operatoren gleicher Klammerungsebene so umgestellt werden, daß ein möglichst effizient kompilierter Ausdruck erreicht wird. In keinem Fall beeinflußt die Umgruppierung den Wert des Ausdrucks.

Mit Hilfe von Klammern können Sie also die Auswertungsreihenfolge in den Ausdrücken erzwingen. Wenn Sie zum Beispiel die Variablen a, b, c und f haben, dann erzwingt der Ausdruck f = a + (b + c), daß (b + c) zuerst ausgeführt und danach das Ergebnis zu a addiert wird.


Fehler und Überläufe

Siehe auch

Die Rangfolge und Auswertungsrichtung der Operatoren sind in Abarbeitungsreihenfolge und Rangfolge der C++Builder Operatoren zusammengefaßt. Während der Auswertung eines Ausdrucks in C++Builder können viele problematische Situationen auftreten, etwa eine Division durch Null oder ein nicht darstellbarer Gleitkommawert. Der Überlauf von Integervariablen wird ignoriert (C verwendet für n-Bit-Register Modulo-2n-Arithmetik). Fehler, die von den mathematischen Bibliotheksfunktionen erkannt werden, können aber mit Standard- oder benutzerdefinierten Routinen behandelt werden. Siehe unter _matherr und signal.


Übersicht: Operatoren

Siehe auch

Operatoren sind Symbole, die bestimmte Verarbeitungsaktionen bewirken, wenn sie auf Variablen und andere Objekte in einem Ausdruck angewandt werden. Die hier beschriebenen Operatoren von C++Builder entsprechen denen des ANSI-Standards:

Arithmetische Operatoren

Bedingungsoperator

Bitweise Operatoren

C++ spezifische Operatoren

Gleichheit

Komma-Operator

Logische Operatoren

Präprocessor

Primärausdruck-Operatoren

Referenzierungs-/Dereferenzierungsoperatoren

Relationale Operatoren

sizeof-Operatoren

Suffixoperatoren

typeid

Zuweisungsoperatoren

Alle Operatoren können überladen werden, mit Ausnahme der folgenden Operatoren:

.	Direkte Elementauswahl in C++

.* 	Dereferenzierung in C++

::	Gültigkeitsbereich-Zugriff in C++

?:	Bedingungsoperator

Je nach Kontext kann ein Operator eine von mehreren Bedeutungen haben. Für den Operator & gibt es beispielsweise die folgenden drei Interpretationsmöglichkeiten:

Bitweises UND (A & B)

Adreßoperator (&A)

Referenz-Modifizierer in C++

Hinweis:	Bei aus mehreren Einzelzeichen bestehenden Operatoren dürfen keine Leerzeichen zwischen ihnen eingefügt werden. Solche Leerzeichen verändern die Bedeutung des Operators und führen zu einem Fehler.


Primärausdruck-Operatoren

 ANSI C benennt als Primärausdrücke Literal (manchmal auch als Konstante bezeichnet), Bezeichner und ( Ausdruck ). Die Sprache C++ erweitert diese Liste primärer Ausdrücke um das Schlüsselwort this, den Zugriffsoperator (::), Name und den Klassendestruktor ~ (Tilde).

Die Syntax der Primärausdrücke ist in der folgenden Liste zusammengefaßt:

 Primärer Ausdruck:

Literal
this (C++ spezifisch)
:: Bezeichner (C++ spezifisch)
:: Operator-Funktionsname (C++ spezifisch)
:: Qualifizierter Name (C++ spezifisch)
(Ausdruck)
Name

Literal:

Integer-Konstante
Zeichen-Konstante
Gleitkomma-Konstante
String-Konstante

Name:

Bezeichner
Operator-Funktionsname (C++ spezifisch)
Konvertierungs-Funktionsname (C++ spezifisch)
~ Klassenname (C++ spezifisch)
Qualifizierter Name (C++ spezifisch)

Qualifizierter Name: (C++ spezifisch)

Qualifizierter Klassenname :: Name

Eine Erläuterung des Primärausdrucks this findet sich unter this (Schlüsselwort).. Das Schlüsselwort this  kann nur innerhalb des Rumpfes einer (Klassen-) Elementfunktion benutzt werden.

Der Zugriffsoperator erlaubt die Bezugnahme auf Typen, Objekte, Funktionen oder Aufzählungen, deren Bezeichner nicht sichtbar sind.

Die runden Klammern um einen Ausdruck verändern den einfachen Ausdruck nicht.

Der Primärausdruck Name bezeichnet Ausdrücke, die auf Operatoren zur Elelmentauswahl folgen (Punkt . und –>  ). Deshalb muß Name entweder ein L-Wert oder eine Funktion sein. Siehe dazu aber auch die Erläuterung der Operatoren zur Elementauswahl

Ein Bezeichner ist ein Primärausdruck, sofern er in geeigneter Weise deklariert wurde. Eine Beschreibung und formale Definition der Bezeichner finden Sie unter Lexikalische Grammatik: Bezeichner

Der Tilde-Operator (~) wird unter dem Thema Destruktoren beschrieben.


Suffix-Operatoren

Siehe auch	Operatoren

Syntax

suffix-ausdruck(<arg-ausdruck-list>)

array deklaration [konstanter-ausdruck]

zusammengesetzte anweisung { anweisungs-liste }

suffix-ausdruck . bezeichner

suffix-ausdruck -> bezeichner

Bemerkungen:

( )	Dient zur Gliederung von Ausdrücken, zur Einfassung von Bedingungsausdrücken und zur Kennzeichnung von Funktionsaufrufen und -parametern.

{ }	Dient zur Markierung von Anfang und Ende einer zusammengesetzten Anweisung.

[ ]	Dient zur Markierung von einfachen und mehrfachen Array-Indizes.

.	Dient zum Zugriff auf Struktur- und Variantenelemente.

->	Dient zum Zugriff auf Struktur- und Variantenelemente.

Die folgenden Suffix-Ausdrücke ermögliches es ihnen, explizite Typumwandlungen in C++ sicherzustellen.

const_cast< T > ( expression )

dynamic_cast< T > ( expression )

reinterpret_cast< T > ( expression )

static_cast< T > ( expression )

Um Laufzeitidentifikation (run-time type identification, RTTI) zu erhalten, verwenden Sie den Operator typeid(). Die Syntax sieht folgendermaßen aus:

typeid(expression)

typeid(type-name)


Array-Indizierungsoperator

Siehe auch	Operatoren

Eckige Klammern [ ] enthalten die Indizes bei ein- oder mehrdimensionalen Arrays. Der Ausdruck

<exp1>[exp2]

ist nach Definition gleichwertig mit

*((exp1) + (exp2))

wobei eine der beiden folgenden Möglichkeiten gilt:

exp1 ist ein Zeiger, und exp2 ist ein Ganzzahlwert.

exp1 ist ein Ganzzahlwert, und exp2 ist ein Zeiger


Klammeroperator

Siehe auch	Operatoren

Syntax

suffix-ausdruck(<arg-ausdruck-liste>)

Bemerkungen:

Runde Klammern () dienen zur

Gliederung von Ausdrücken

Einfassung von Bedingungsausdrücken

Kennzeichnung von Funktionsaufrufen und -parametern

Der Wert eines Funktionsaufrufausdrucks wird, falls er einen hat, von der return-Anweisung in der Funktionsdefinition bestimmt.

Dies ist ein Aufruf, der durch den Suffixausdruck spezifizierten Funktion.

arg-ausdruck-liste ist eine durch Kommas getrennte Liste von Ausdrücken beliebigen Typs, die die Funktionsargumente darstellen.


Operator für direkte Elementauswahl

Siehe auch	Beispiel	Operatoren

Syntax

suffix-ausdruck . bezeichner

suffix-ausdruck muß von einem Struktur- oder Variantentyp sein.

bezeichner muß der Name eines Elements dieser Struktur oder Variante sein.

Bemerkungen:

Dieser Auswahloperator (.) ermöglicht den Zugriff auf Elemente von Strukturen und Varianten.

Angenommen, das Objekt s ist vom Strukturtyp S und sptr ist ein Zeiger auf S. Dann ist, falls m der Name eines Elements vom Typ M (deklariert in S) ist, der folgende Ausdruck

s.m

vom Typ M und repräsentiert das Elementobjekt m in s.


Beispiele

struct meinestruct {

   int i

   char str[21]

   double d

} s, *sptr=&s

 ...

s.i = 3          // Zuweisung an das Element i von meinestruct s.

Der Ausdruck s.m ist ein L-Wert, vorausgesetzt, s ist kein L-Wert und m ist kein Array-Typ.

Wenn die Struktur B ein Element vom Typ Struktur A enthält, kann auf die Elemente von A per zweimaliger Anwendung des Elementauswahloperators zugegriffen werden.


-> Operator für indirekte Elementauswahl (Zeigeroperator)
Operator für indirekte Elementauswahl

Siehe auch	Beispiel	Operatoren

Syntax

suffix-ausdruck -> bezeichner

suffix-ausdruck muß ein Zeiger auf eine Struktur oder Variante sein.

bezeichner muß der Name eines Elements dieser Struktur bzw. Variante sein.

Der Ausdruck bestimmt ein Element eines Struktur- oder Variantenobjekts. Der Wert des Ausdrucks ist der Wert des ausgewählten Elements. Dies ist dann und nur dann ein L-Wert, wenn der Suffixausdruck ein L-Wert ist.

Bemerkungen:

Dieser Auswahloperator (->) ermöglicht den Zugriff auf Elemente von Strukturen und Varianten.

Angenommen, das Objekt s ist vom Strukturtyp S und sptr ist ein Zeiger auf S. Dann ist, falls m der Name eines Elements vom Typ M (deklariert in S) ist, der folgende Ausdruck

sptr->m

vom Typ M und repräsentiert das Elementobjekt m in s.

Der Ausdruck

s->sptr

ist also ein nützliches Synonym für(*sptr).m


-> Beispiel

struct mystruct {

   int i

   char str[21]

   double d

} s, *sptr=&s

  .

  .

  .

sptr->d = 1.23   // Zuweisung an das Element d von mystruct s

Der Ausdruck sptr->m ist ein L-Wert, solange m nicht ein Array-typ ist.

Wenn die Struktur B ein Element vom Typ Struktur A enthält, kann auf die Elemente von A per zweimaliger Anwendung des Elementauswahloperators zugegriffen werden.


Inkrement-/Dekrement-Operatoren

Operatoren

Inkrement-Operator  ( ++ )

Syntax

suffix-ausdruck ++         (postinkrement)

++ unärer-ausdruck         (preinkrement)

Der als Operand dienende Ausdruck muß von einem skalaren Typ (arithmetischer Typ oder Zeiger) sein; außerdem muß er ein modifizierbarer L-Wert sein.

Postinkrement-Operator

Für den Postinkrement-Operator gilt, daß der Wert des gesamten Ausdrucks gleich dem Wert des Suffixausdrucks vor der Inkrementierung ist.

Nachdem der Suffixausdruck ausgewertet ist, wird der Operand um 1 erhöht.

Präinkrement-Operator

Der Operand wird um 1 erhöht, bevor sein Wert ausgewertet wird. Der Wert des gesamten Ausdrucks ist gleich dem erhöhten Wert des Operanden.

Der Erhöhungswert wird dem Typ des Operanden angepaßt.

Für Zeigertypen gelten die Regeln der Zeigerarithmetik.

Dekrement-Operator  ( -- )

Syntax:

suffix-ausdruck --        (postdekrement)

-- unärer-ausdruck        (predekrement)

Für den Dekrement-Operator gelten dieselben Regeln wie für den Inkrementoperator, mit dem einzigen Unterschied, daß der Operand um 1 vermindert wird, bevor bzw. nachdem der gesamte Ausdruck ausgewertet wird.


Unäre Operatoren

Siehe auch	Operatoren

Syntax

<unärer-operator> <unärer-ausdruck>

oder

<unärer-operator> <typ><unärer-ausdruck>

Bemerkungen:

Unäre Operatoren gruppieren von rechts nach links.

C++Builder kennt die folgenden unären Operatoren:

!   Logische Negation

*   Dereferenzierung

++  Inkrement

~   Bitweise Negation

--  Dekrement

-   Unäres Minus

+   Unäres Plus


Referenzierungs-/Dereferenzierungsoperatoren

Siehe auch	Operatoren

Syntax

& cast-ausdruck

* cast-ausdruck

Bemerkungen:

Die Operatoren & und * arbeiten zusammen, um Zeiger zu referenzieren bzw. zu dereferenzieren, die an Funktionen übergeben werden.

Referenzierungsoperator ( & )

Der Referenzierungsoperator wird verwendet, um die Adresse eines Zeigers an eine Funktion außerhalb von main() zu übergeben.

Der Operand (cast-Ausdruck) muß eins der folgenden sein:

ein Funktionsdesignator

ein L-Wert, der ein Objekt bezeichnet, das kein Bitfeld ist und nicht die Speicherklasse register hat.

Hat der Operand den Typ typ, so ist das Ergebnis ein Zeiger auf typ.

Einige Bezeichner, die keine L-Werte sind, wie Funktionsnamen und Array-Namen, werden automatisch konvertiert zu pointer-to-X-Typen, wenn sie in bestimmten Zusammenhängen auftreten. Der Operator & kann mit solchen Objekten gebraucht werden, aber dessen Gebrauch ist redundant und daher entmutigend.

Ein Beispiel:

   T t1 = 1, t2 = 2;

   T *ptr = &t1;      // Initialisierter Zeiger

   *ptr = t2;           // Gleicher Effekt wie t1 = t2

T *ptr = &t1 wird behandelt als

   T *ptr;

   ptr = &t1;

Also wird ptr oder *ptr zugewiesen. Sobald ptr initialisiert worden ist mir der Adresse &t1, kann er sicher dereferenziert werden, damit sich der L-Wert *ptr ergibt.

Dereferenzierungsoperator ( * )

Der Dereferenzierungsoperator kann in einem Variablenausdruck verwendet werden, um einen Zeiger zu erzeugen. Außerdem kann dieser Operator in external-Funktionen benutzt werden, um den Wert eines Zeigers, der per Referenz übergeben wurde, zu erhalten.

Ist der Operand vom Typ "Zeiger auf Funktion", so ist das Ergebnis ein Funktionsdesignator.

Ist der Operand ein Zeiger auf ein Objekt, so ist das Ergebnis ein L-Wert, der das Objekt bezeichnet.

Unter jeder der folgenden Bedingungen ist das Ergebnis der Dereferenzierung undefiniert:

1.	Der Cast-Ausdruck ist ein Nullzeiger.

2.	Der Cast-Ausdruck liefert die Adresse einer auto-Variablen und die Ausführung des betreffenden Blocks ist bereits beendet.

Hinweis:	Mit & wird auch der Operator für das bitweise UND bezeichnet.

Das Zeichen * kann auch der Multiplikationsoperator sein.


Plus- und Minus-Operatoren

Siehe auch	Operatoren

Unär

In den folgenden unären Plusausdrücken

+ cast-ausdruck

- cast-ausdruck

muß der Operand cast-ausdruck von einem arithmetischen Typ sein.

Wirkung:

+ cast-ausdruck	Wert des Operanden nach etwaigen erforderlichen Ganzzahltyp-Erweiterungen.

- cast-ausdruck	Negativer Wert des Operanden nach etwaigen erforderlichen Ganzzahltyp-Erweiterungen.

Binär

Syntax:

additions-ausdruck + multiplikations-ausdruck

additions-ausdruck - multiplikations-ausdruck

Zulässige Operandentypen für op1 und op2:

1.	op1 und op2 sind beide von einem arithmetischen Typ.

2.	op1 ist ein Ganzzahltyp, und op2 ist ein Zeiger auf ein Objekt.

3.	op2 ist ein Ganzzahltyp, und op1 ist ein Zeiger auf ein Objekt.

Im Fall 1 werden die Operanden den arithmetischen Standardkonvertierungen unterworfen, und das Ergebnis ist die arithmetische Summe der Operanden.

In den Fällen 2 und 3 gelten die Regeln für Zeigerarithmetik.

Zulässige Operandentypen für op1 - op2:

1.	op1 und op2 sind beide von einem arithmetischen Typ.

2.	op1 und op2 sind Zeiger auf kompatible Objekttypen.

3.	op1 ist ein Zeiger auf ein Objekt, und op2 ist ein Ganzzahltyp.

Im Fall 1 werden die Operanden den arithmetischen Standardkonvertierungen unterworfen, und das Ergebnis ist die arithmetische Summe der Operanden.

In den Fällen 2 und 3 gelten die Regeln für Zeigerarithmetik.

Hinweis:	Der unqualifizierte Typ typ gilt in diesem Zusammenhang als kompatibel zu den qualifizierten Typen const typ, volatile typ und const volatile typ.


Arithmetische Operatoren

Siehe auch	Operatoren

Syntax

+ cast-ausdruck

- cast-ausdruck

additions-ausdruck + multiplikations-ausdruck

additions-ausdruck - multiplikations-ausdruck

multiplikations-ausdruck * cast-ausdruck

multiplikations-ausdruck / cast-ausdruck

multiplikations-ausdruck % cast-ausdruck

suffix-ausdruck ++         (postinkrement)

++ unärer-ausdruck         (preinkrement)

suffix-ausdruck --         (postdekrement)

-- unärer-ausdruck         (predekrement)

Bemerkungen:

Die arithmetischen Operatoren dienen zur Ausführung mathematischer Berechnungen.

Die unären Operatoren + und - geben dem Cast-Ausdruck einen positiven oder negativen Wert.

Die Operatoren + (Addition), - (Subtraktion), * (Multiplikation) und / (Division) bewirken die Ausführung der jeweiligen Grundrechenart, und zwar bei allen Ganzzahl- und Gleitkomma-Datentypen.

Der Operator % (Modulo-Operator) liefert den Rest einer ganzzahligen Division, er kann nicht auf Gleitkommatypen angewandt werden.

Der Operator ++ (Inkrement) addiert die Zahl 1 zum Wert des Ausdrucks. Beim Suffix-Inkrement-Operator findet diese Addition statt, nachdem der Ausdruck ausgewertet wurde, beim Präfix-Inkrement-Operator dagegen vor der Auswertung.

Der Operator -- (Dekrement) subtrahiert die Zahl 1 vom Wert des Ausdrucks. Beim Suffix-Dekrement-Operator findet diese Subtraktion statt, nachdem der Ausdruck ausgewertet wurde, beim Präfix-Dekrement-Operator dagegen vor der Auswertung.


Binäre Operatoren

Siehe auch	Operatoren

In C++Builder gibt es die folgenden binären Operatoren:

Arithmetische Operatoren	+	Binäres Plus (Addition)

- 	Binäres Minus (Subtraktion)

*	Multiplikation

/	Division

% 	Divisionsrest (Modulus)

Bitweise Operatoren	<<	Linksschieben

>>	Rechtsschieben

&	Bitweises UND

^ 	Bitweises XOR (exklusives ODER)

|  	Bitweises inklusives ODER

Logische Operatoren	&&  	Logisches UND

||  	Logisches ODER

Zuweisungsoperatoren	Zuweisung

*= 	Zuweisung mit Multiplikation

/=  	Zuweisung mit Division

%=  	Zuweisung mit Ermittlung des Divisionsrests (Modulus)

+=  	Zuweisung mit Addition

-=  	Zuweisung mit Subtraktion

<<= 	Zuweisung mit Linksschieben

 >>=	Zuweisung mit Rechtsschieben

&=  	Zuweisung mit bitweisem UND

^=  	Zuweisung mit bitweisem exklusivem ODER

|=  	Zuweisung mit  bitweisem ODER

Relationale Operatoren	<   	Kleiner als

>   	Größer als

<=  	Kleiner als oder gleich

>=  	Größer als oder gleich

==  	Gleich

!=  	Ungleich

Elementauswahl-Operatoren. 	Direkte Elementauswahl

->  	Indirekte Elementauswahl

Klassenelementzugriffs-Operat.	: 	Zugriff auf Gültigkeitsbereich

.*  	Dereferenzierungszeiger auf Klassenelement

->* 	Dereferenzierungszeiger auf Klassenelement

Bedingungsoperator	? : 	Ein ternärer Operator (drei Operanden), Beispiel:

      a ? x : y 	"if a then x else y"

Komma-Operator	Ausdruck auswerten


Multiplikative Operatoren

Operatoren

Syntax

multiplikations-ausdruck * cast-ausdruck

multiplikations-ausdruck / cast-ausdruck

multiplikations-ausdruck % cast-ausdruck

Bemerkungen

Es gibt drei multiplikative Operatoren:

*	(Multiplikation)

/	(Division)

%	(Modulus, Rest)

Auf die Operanden werden die arithmetischen Standardumwandlungen angewendet.

(op1 * op2)  Produkt der beiden Operanden

(op1 / op2)  Quotient (op1 dividiert durch op2)

(op1 % op2)  Rest bei der Division (op1 dividiert durch op2)

Für die Operatoren / und % muß op2 ungleich Null sein, op2 = 0 ergibt einen Fehler (man kann nicht durch Null teilen).

Falls op1 und op2 Ganzzahlwerte sind und der Quotient nicht ganzzahlig ist, gilt folgendes:

1.	Wenn op1 und op2 gleiche Vorzeichen haben, so ergibt op1 / op2 die größte ganze Zahl, die kleiner als der wahre Quotient ist, und op1 % op2 erhält das Vorzeichen von op1.

2.	Wenn op1 und op2 entgegengesetzte Vorzeichen haben, so ergibt op1 / op2 die kleinste ganze Zahl, die größer als der exakte Quotient ist, und op1 % op2 erhält das Vorzeichen von op1.

Hinweis:	Es wird immer in Richtung auf Null gerundet.

Das Zeichen * ist kontextabhängig und kann auch als der Zeigerreferenzierungsoperator interpretiert werden.


Bitweise Operatoren

Siehe auch	Operatoren

Syntax

AND-ausdruck &  gleichheits-ausdruck

exclusiv-ODER-ausdruck ^ UND-ausdruck

inclusiv-ODER-ausdruck exclusiv-ODER-ausdruck

~cast-ausdruck

schiebe-ausdruck << additions-ausdruck

schiebe-ausdruck >> additions-ausdruck

Bemerkungen:

Die bitweisen Operatoren dienen zur Änderung einzelner Bits anstelle einer kompletten Zahl.

Operator	Beschreibung

&	Bitweises UND; vergleicht paarweise je zwei korrespondierende Bits und setzt das entsprechende Bit im Ergebnis auf 1, wenn beide Bits 1 sind, andernfalls auf 0.

|	Bitweises inklusives ODER; vergleicht paarweise je zwei korrespondierende Bits und setzt das entsprechende Bit im Ergebnis auf 1, wenn eins oder beide Bits 1 sind, andernfalls auf 0.

^	Bitweises exklusives ODER; vergleicht paarweise je zwei korrespondierende Bits und setzt das entsprechende Bit im Ergebnis auf 1, wenn beide Bits unterschiedlich sind, andernfalls auf 0.

~	Bitweise Negation; invertiert jedes Bit. Dieser Operator wird auch zum Erzeugen von Destruktoren benutzt.

>>	Bitweises Schieben nach rechts; verschiebt alle Bits nach rechts, wobei das jeweils ganz rechte Bit verworfen und das ganz linke auf 0 gesetzt wird.

<<	Bitweises Schieben nach links; verschiebt alle Bits nach links, wobei das jeweils ganz linke Bit verworfen und das ganz rechte auf 0 gesetzt wird.

Beide Operanden eines bitweisen Operators müssen einen Ganzzahltyp haben.

Bitwert	Ergebnisse der verschiedenen Operatoren

E1	E2	E1 & E2	E1 ^ E2	E1 | E2

0	0	0	0	0

1	0	0	1	1

0	1	0	1	1

1	1	1	0	1

Hinweis:	Die Operatoren &, >> und << sind kontextabhängig. & kann auch der Zeigerreferenzoperator sein

>>  kann auch der Eingabe-Operator in einem Ein-/Ausgabeausdruck sein

<<  kann auch der Ausgabe-Operator in einem Ein-/Ausgabeausdruck sein


Relationale Operatoren

Siehe auch	Operatoren

Syntax

relationaler-ausdruck  <   schiebe-ausdruck

relationaler-ausdruck  >   schiebe-ausdruck

relationaler-ausdruck  <=  schiebe-ausdruck

relationaler-ausdruck  >=  schiebe-ausdruck

Bemerkungen:

Die relationalen Operatoren werden für die Prüfung von Ausdrücken auf Gleichheit oder Ungleichheit benutzt. Wenn die Aussage wahr ist, ergibt der relationale Ausdruck den Wert Wahr (1), andernfalls Falsch (0).

>	größer als

<	kleiner als

>=	größer als oder gleich

<=	kleiner als oder gleich

In dem Ausdruck

E1 <operator> E2

müssen die Operanden eine der folgenden Bedingungen erfüllen:

1.	E1 und E2 sind beides arithmetische Typen.

2.	E1 und E2 sind beides Zeiger auf qualifizierte oder unqualifizierte Versionen von kompatiblen Typen.

3.	E1 oder E2 ist ein Zeiger auf ein Objekt oder auf einen unvollständigen Typ, und der jeweils andere Ausdruck ist ein Zeiger auf eine qualifizierte oder unqualifizierte Version des Typs void.

4.	E1 oder E2 ist ein Zeiger, und der jeweils andere Ausdruck ist eine Nullzeiger-Konstante.


Gleichheitsoperatoren

Siehe auch

Es gibt zwei Gleichheitsoperatoren: == und !=. Sie überprüfen die Gleichheit bzw. die Ungleichheit von arithmetischen Werten oder Zeigern. Die Anwendungsregeln sind denen für die relationalen Operatoren sehr ähnlich.

Hinweis:	Beachten Sie, daß == und != eine niedrigere Abarbeitungspriorität haben als die relationalen Operatoren <, >, <=, und >=. Mit == und != können Sie auch bestimmte Zeigertypen auf Gleichheit und Ungleichheit miteinander vergleichen, bei denen die relationalen Operatoren nicht erlaubt sind.

Die Syntax lautet:

Gleichheitsausdruck

Relationaler-Ausdruck

Gleichheitsausdruck == Relationaler-Ausdruck

Gleichheitsausdruck != Relationaler-Ausdruck


Logische Operatoren

Operatoren

Syntax

logischer-UND-ausdruck  && inklusiv-ODER-ausdruck

logischer-ODER-ausdruck   || logischer-UND-ausdruck

! cast-ausdruck

Bemerkungen:

Operanden in logischen Ausdrücken müssen von einem skalaren Typ sein.

&&	Logisches UND; liefert das Ergebnis Wahr (1), wenn beide Ausdrücke bei der Auswertung einen Wert ungleich Null ergeben, andernfalls ist das Ergebnis Falsch (0). Wenn der erste Ausdruck Falsch ergibt, wird der zweite nicht ausgewertet.

||	Logisches ODER; iefert das Ergebnis Wahr (1), wenn mindestens einer der beiden Ausdrücke bei der Auswertung einen Wert ungleich Null ergibt, andernfalls ist das Ergebnis Falsch (0). Wenn der erste Ausdruck Wahr ergibt, wird der zweite nicht ausgewertet.

!	Logische Negation; liefert das Ergebnis Wahr (1), wenn der gesamte Ausdruck einen Wert von Null hat, andernfalls ist das Ergebnis Falsch (0). Der Ausdruck !E ist gleichwertig mit (0 == E).


Bedingungsoperator

Operatoren

Syntax

logischer-OR-ausdruck ? ausdruck : conditional-ausdruck

Bemerkungen:

Der Bedingungsoperator ?: ist ein ternärer Operator.

Im Ausdruck E1 ? E2 : E3 wird E1 zuerst ausgewertet. Wenn sein Wert ungleich Null (Wahr) ist, so wird danach E2 ausgewertet und E3 ignoriert. Wenn E1 Null (Falsch) ergibt, so wird E3 ausgewertet und E2 ignoriert.

Das Ergebnis von E1 ? E2 : E3 ist entweder der Wert von E2 oder von E3, abhängig davon, welcher dieser Werte ausgewertet wurde.

E1 muß ein Ausdruck von skalarem Typ sein. E2 und E3 müssen einer der folgenden Regeln entsprechen:

1.	Beide sind arithmetische Typen. In diesem Fall unterliegen E2 und E3 den arithmetischen Standardkonvertierungen und das Ergebnis entspricht dem üblichen Ergebnistyp dieser Konvertierungen.

2.	Beide Operanden sind kompatible Struktur- oder Varianten-Typen. Das Ergebnis ist vom Struktur- oder Varianten-Typ von E2 und E3.

3.	Beide Operanden sind vom Typ void. Das Ergebnis ist vom Typ void.

4.	Beide Operanden sind Zeiger auf qualifizierte oder unqualifizierte kompatible Typen. Der Typ des Ergebnisses ist ein Zeiger auf einen Typ, der alle Typqualifizierer der Typen, auf die beide Operanden zeigen, besitzt.

5.	Einer der Operanden ist ein Zeiger, der andere eine Nullzeiger-Konstante. Der Typ des Ergebnisses ist ein Zeiger auf einen Typ, der alle Typqualifizierer der Typen, auf die beide Operanden zeigen, besitzt.

6.	Einer der Operanden ist ein Zeiger auf ein Objekt oder auf einen unvollständigen Typ, der andere ist ein Zeiger auf einen qualifizierten oder unqualifizierten void-Typ. Das Ergebnis ist vom Typ des Operanden, der nicht auf void zeigt.


Zuweisungsoperatoren

Siehe auch	Operatoren

Syntax

unärer-ausdruck  zuweisungs-operator  zuweisungs-ausdruck

Bemerkungen:

Es gibt folgende Zuweisungsoperatoren:

=    *=     /=     %=    +=    -=

<<=  >>=    &=     ^=    |=

Der Operator = ist dabei der einzige einfache Zuweisungsoperator, die anderen sind kombinierte Zuweisungsoperatoren.

Im Ausdruck E1 = E2 muß E1 ein modifizierbarer L-Wert. sein. Der Zuweisungsausdruck selbst ist kein L-Wert.

Der Ausdruck

E1 op= E2

hat den gleichen Effekt wie

E1 = E1 op E2

außer, daß der L-Wert E1 im ersten Fall nur einmal ausgewertet wird. Beispiel: E1 += E2 ist gleichwertig mit E1 = E1 + E2.

Der Wert des Zuweisungsausdrucks nach Ausführung der Zuweisung ist E1.

Bei einfachen sowie bei kombinierten Zuweisungen müssen die Operanden E1 und E2 einer der folgenden Bedingungen entsprechen:

1.	E1 ist eine qualifizierte oder unqualifizierte Version eines arithmetischen Typs, und E2 hat einen arithmetischen Typ.

2.	E1 ist eine qualifizierte oder unqualifizierte Version eines Struktur- oder Varianten-Typs, der mit dem Typ von E2 kompatibel ist.

3.	E1 und E2 sind Zeiger auf qualifizierte oder unqualifizierte Versionen von kompatiblen Typen, und der Typ, auf den der linke Zeiger zeigt, hat alle Qualifizierer des Typs, auf den der rechte Zeiger zeigt.

4.	Einer der Operanden E1 und E2 ist ein Zeiger auf ein Objekt oder auf einen unvollständigen Typ, und der andere Operand ist ein Zeiger auf einen qualifizierten oder unqualifizierten void-Typ. Der Typ, auf den der linke Zeiger zeigt, hat alle Qualifizierer des Typs, auf den der rechte Zeiger zeigt.

5.	E1 ist ein Zeiger, und E2 ist die Nullzeiger-Konstante (NULL).

Hinweis: Leerzeichen innerhalb der Operatoren für kombinierte Zuweisung (zum Beispiel +<Leerzeichen>=) führen zu Fehlern.


Komma-Interpunktionszeichen und Komma-Operator

Siehe auch	Operatoren

Syntax

ausdruck , zuweisungs-ausdruck

Bemerkungen:

Das Komma trennt die Elemente einer Liste von Funktionsargumenten.

Außerdem wird das Komma auch als Operator in Kommaausdrücken verwendet. Es ist zulässig, diese beiden Verwendungsarten zu mischen, aber es müssen dann Klammern zur Klarstellung gesetzt werden.

Der linke Operand E1 wird als void-Ausdruck ausgewertet. Danach wird E2 ausgewertet und liefert den Wert und den Typ des Kommaausdrucks. Dies kann auch rekursiv geschehen; bei dem Ausdruck

E1, E2, ..., En

wird von links nach rechts jeder Einzelausdruck Ei ausgewertet, und der Wert und der Typ von En liefern das Ergebnis des gesamten Ausdrucks.

Zur Vermeidung von Mehrdeutigkeiten bei der Verwendung von Kommas in Funktionsargument- oder Initialisierer-Listen müssen Klammern eingesetzt werden. Im folgenden Beispiel

func(i, (j = 1, j + 4), k);

wird die Funktion func mit drei Argumenten (i, 5, k), nicht mit vier, aufgerufen.


C++ spezifische Operatoren

Siehe auch	Operatoren

Folgende Operatoren sind spezifische Operatoren von C++:

::	Gültigkeitsbereichzugriffsoperator

.*	Dereferenzierungszeiger auf Klassenelemente

->*	Dereferenzierungszeiger auf Zeiger auf Klassenelemente

const_cast 	Fügt hinzu, oder entfernt den const- oder volatile-Modifizierer von einen Typ

delete 	Gibt Speicher dynamisch frei

dynamic_cast 	Wandelt einen Zeiger in einen gewünschten Typen um

new 	Weist Speicher dynamisch zu

reinterpret_cast 	replaces casts for conversions that are unsafe or implementation dependent.

static_cast 	converts a pointer to a desired type

typeid 	gets run-time identification of types and expressions

Der Gültigkeitsbereichzugriffsoperator :: (zwei Doppelpunkte) ermöglicht den Zugriff auf einen global oder dateiweit gültigen Namen sogar dann, wenn dieser durch eine lokale Neudeklaration überdeckt wird.

Die Operatoren .* und ->* dienen zum Dereferenzieren von Zeigern bzw. Zeigern auf Zeiger auf Klassenelemente.


Anweisungen

Siehe auch

Anweisungen steuern die Ablaufkontrolle während der Ausführung eines Programms. Enthält das Programm keine Sprung-und Auswahlanweisungen, dann werden Anweisungen nacheinander (sequentiell), das heißt, in der im Quelltext vorgegebenen Reihenfolge ausgeführt. C++Builder Anweisungen zeigt eine Auflistung der Syntax für verschiedene Anweisungen.

C++Builder Anweisungen

Anweisung:

Anweisung mit Label

Blockanweisung

Ausdrucksanweisung

Auswahlanweisung

Wiederholungsanweisung

Sprunganweisung

asm-Anweisung

Deklaration (C++ spezifisch)

Anweisung mit Label:

Bezeichner  :  Anweisung

case  Konstantenausdruck  :  Anweisung

default  :  Anweisung

Blockanweisung:

{  <Deklaration-Liste>  <Anweisungsliste>  }

Deklarationsliste:

Deklaration

Deklarationsliste  Deklaration

Anweisungsliste:

Anweisung

Anweisungsliste  Anweisung

Ausdrucksanweisung:

<Ausdruck> ;

asm-Anweisung:

asm  Symbole  Zeilenvorschub

asm  Symbole;

asm  { Symbole; <Symbole;>= <Symbole;>}

Auswahlanweisung:

if  ( Ausdruck )  Anweisung

if  ( Ausdruck )  Anweisung else  Anweisung

switch  ( Ausdruck )  Anweisung

Wiederholungsanweisung:

while  (  Ausdruck )  Anweisung

do  Anweisung  while  ( Ausdruck ) ;

for  (for-init-Anweisung <Ausdruck>  ;  <Ausdruck>) Anweisung

for-init-Anweisung:

Ausdruck-Anweisung

Deklaration (C++ spezifisch)

Sprunganweisung:

goto  Bezeichner ;

continue  ;

break  ;

return  <Ausdruck> ;


Blockanweisungen

Siehe auch

Eine Blockanweisung oder ein Block besteht aus einer (möglicherweise leeren) Liste von Anweisungen, die in geschweifte Klammern ({ }) eingeschlossen sind. Syntaktisch kann eine Blockanweisung eine einzelne Anweisung enthalten, aber er hat auch Auswirkungen auf den Gültigkeitsbereich von Bezeichnern. Der Gültigkeitsbereich eines im in einem Block deklarierten Bezeichners beginnt beim Deklarationspunkt und endet an der schließenden geschweiften Klammer. Blockanweisungen können beliebig tief geschachtelt werden.


Anweisungen mit Sprungmarke (Label)

Siehe auch

Es gibt zwei verschiedene Möglichkeiten, eine Anweisung mit einem Label (einem Sprungziel) zu versehen:

Label-Bezeichner : Anweisung

Der Label-Bezeichner dient als Ziel für eine unbedingte goto-Anweisung. Label-Bezeichner besitzen ihren eigenen Namensbereich und ihr Gültigkeitsbereich erstreckt sich auf die ganze Funktion. In C++ können sowohl Deklarations- als auch Nicht-Deklarationsanweisungen mit einem Label versehen werden.

case Konstantenausdruck : Anweisung

default : Anweisung

Sprungziele in case- und default-Anweisungen werden nur in Verbindung mit Schalter-Anweisungen verwendet.


Ausdrucksanweisungen

Siehe auch

Jeder Ausdruck, dem ein Semikolon folgt, stellt eine Ausdrucksanweisung dar:

<Ausdruck>;

C++Builder führt eine Ausdruckanweisung durch Auswerten des Ausdrucks aus. Alle Nebeneffekte dieser Auswertung werden vor der Auswertung der nächsten Anweisung erzeugt. Die meisten Ausdruckanweisungen sind Zuweisungsanweisungen oder Funktionsaufrufe.

Die Null Anweisung ist ein Sonderfall. Sie besteht nur aus einem Semikolon (;). Die Null-Anweisung hat keine Auswirkung, ist aber in manchen Fällen ganz sinnvoll, zum Beispiel wenn die Syntax von C++Builder eine Anweisung erwartet, das Programm aber keine benötigt.


Auswahlanweisungen

Siehe auch

Auswahl- oder Steuerflußanweisungen bestimmen den Programmablauf durch die Auswertung bedingter Ausdrucke. Es gibt zwei Arten von Auswahlanweisungen: die Anweisung if...else und die Anweisung switch.


Wiederholungsanweisungen

Siehe auch

Wiederholungsanweisungen ermöglichen die Erzeugung von Schleifen. In C++Builder gibt es drei Arten von Schleifen: while-, do while- und for-Schleifen.


Sprunganweisungen

Siehe auch

Bei der Ausführung einer Sprunganweisung wird die Programmsteuerung unbedingt übertragen. Es gibt vier Sprunganweisungen: break, continue, goto und return.


C++ Spezifikation

Siehe auch

C++Builder ist eine objektorientierte Programmiersprache, die auf C basiert. Es ist daher möglich, C-Programme unter C++ zu compilieren. Die Übernahme von C++ Programmen in eine C-Umgebung funktioniert aber nur, wenn keine speziellen Eigenschaften von C++ verwendet werden. Einige Situationen erfordern besondere Vorsicht. Wird z.B. eine Funktion func gleichen Namens in C zweimal mit verschiedenen Argumenttypen definiert, erzeugt dies einen Fehler. In C++wird die Funktion dann als überladene Funktion interpretiert. Ob dies erlaubt ist oder nicht, hängt von bestimmten Voraussetzungen ab.

Obwohl C++ neue Schlüsselwörter und Operatoren zur Handhabung von Klassen eingeführt hat, können einige Möglichkeiten von C++ auch außerhalb von Klassen verwendet werden. In diesem Kapitel werden zunächst die Aspekte von C++ behandelt, die unabhängig von Klassen benutzt werden können. Im weiteren Verlauf werden die Besonderheiten von Klassen und Klassenmechanismen behandelt.

Einzelheiten zum Kompilieren von C- und C++ Programmen mit Ausnahmebehandlung finden Sie unter C++ Ausnahmebehandlung und unter C-basierte strukturierte Ausnahmen.


Überblick über Namensbereiche

Siehe auch

Die meisten nicht trivialen Anwendungen bestehen aus mehreren Quelltextdateien. Mehrere Entwickler können die Autoren dieser Dateien sein und sie verwalten. Die verschiedenen Dateien werden schließlich jedoch zum Endprodukt zusammengestellt und gelinkt. Die Dateiorganisation macht es normalerweise erforderlich, daß alle Namen, die nicht in einen definierten Namensbereich eingekapselt sind (wie z.B. der Rumpf einer Funktion oder Klasse oder eine Übersetzungseinheit), denselben globalen Namensbereich verwenden. Beim Linken von verschiedenen Modulen werden daher mehrere Namensdefinitionen entdeckt, die irgendwie unterschieden werden müssen. Dieses Problem der Namenskonflikte im globalen Gültigkeitsbereich löst der Namensbereichmechanismus von C++.

Der Namensbereichmechanismus sieht die Unterteilung der Anwendung in mehrere Subsysteme vor. Jedes Subsystem kann innerhalb seines eigenen Gültigkeitsbereichs definieren und arbeiten. Entwickler haben so die Möglichkeit, beliebige Bezeichner einzusetzen, die innerhalb des Subsystems sinnvoll erscheinen. Sie müssen sich nicht darum kümmern, ob diese Bezeichner bereits von anderen Entwicklern verwendet werden. Der Gültigkeitsbereich des Subsystems wird anwendungsweit durch einen eindeutigen Bezeichner festgelegt.

Sie müssen zwei Schritte ausführen, um die Namensbereiche von C++ zu verwenden. Kennzeichnen Sie zunächst einen Namensbereich mit dem Schlüsselwort namespace eindeutig. Greifen Sie dann auf die Elemente des gekennzeichneten Namensbereichs zu, indem Sie das Schlüsselwort using angeben.


Definieren von Namensbereichen

Siehe auch

Zur Definition eines Namensbereichs wird die folgende Grammatik verwendet:

   ursprünglicher-Namensbereich-Name:

      Bezeichner

   Namensbereich-Definition:

       ursprüngliche-Namensbereich-Definition

      Erweiterungs-Namensbereich-Definition

      unbenannte-Namensbereich-Definition

Grammatisch gesehen gibt es drei Möglichkeiten, den Namensbereich über das Schlüsselwort zu definieren:

   ursprüngliche-Namensbereich-Definition:

         namespace Bezeichner { Namensbereichs-Rumpf }

   Erweiterungs-Namensbereich-Definition:

         namespace ursprünglicher-Namensbereich-Name { Namensbereichs-Rumpf }

 unbenannte Namensbereich-Definition:

         namespace { Namensbereichs-Rumpf }

Der Rumpf ist eine wahlfreie Deklarationsfolge. Die Grammatik sieht folgendermaßen aus:

 Namensbereichs-Rumpf:

      Deklarationsfolge opt


Beispiel
Beispiel für die Erweiterung von Namensbereichen

// Ein Beispiel für die using-Anweisung

#include <iostream.h>

   struct S { };

   class C  { };

   namespace ALPHA {  //  ALPHA ist ein ursprünglicher Bezeichner.

      void g(struct S) {

         cout << "Processing a structure argument" << endl;

         }

      }

   using ALPHA::g;  // using-Deklaration

   /*** Nach der obigen using-Daklaration werden nachfolgende Versuche,

        die Funktion g() zu überladen ignoriert. ***/

   namespace ALPHA { // Erweitern des ALPHA-Namensbereichs

      void g( C& ) { // überladene Version der Funktion

         cout << "Processing a class argument." << endl;

         }

      }

   int main() {

      S mystruct;

      C myclass;

      g(mystruct);

      // Der folgende Funktionsaufruf schlägt während der Compilierungszeit

      // fehl, da es für diesen Fall keine überladene Version gibt.

//      g(myclass);

      return 0;

   }

Ausgabe:

Processing a structure argument


Deklarieren von Namensbereichen

Siehe auch

Eine Originaldeklaration des Namensbereichs sollte keinen Bezeichner enthalten, der bereits zuvor als globaler Bezeichner benutzt wurde.

    namespace ALPHA {  /* ALPHA ist der Bezeichner dieses Namensbereichs. */

      /* Ihre Programm-Deklarationen */

      long double LD;

      float f(float y) { return y; }

      }

Bezeichner für Namensbereiche müssen in allen Übersetzungeinheiten bekannt sein, auf deren Elemente Sie zugreifen möchten.


Alias für Namensbereiche

Es ist möglich, mit einem alternativen Namen auf den Namensbereichbezeichner zu verweisen. Ein Alias ist sinnvoll, wenn Sie sich auf einen Bezeichner beziehen müssen, der lang und unhandlich ist.

  namespace BORLAND_INTERNATIONAL {

     /* namespace-body */

     namespace NESTED_BORLAND_INTERNATIONAL {

        /* namespace-body */

        }

     }

  // Alias-Namensbereich

  namespace BI = BORLAND_INTERNATIONAL;

  // Verwenden Sie den Zugriffs-Qualifizierer als Alias für einen verschachtelten Namensbereich

  namespace NBI = BORLAND_INTERNATIONAL::NESTED_BORLAND_INTERNATIONAL;


Erweitern der Namensbereiche

Beispiel

Namensbereiche sind nicht zusammenhängend und können weitergehend bearbeitet werden. Wenn Sie einen Namensbereich neu deklarieren, wird der ursprüngliche Bereich erweitert, indem neue Deklarationen hinzugefügt werden. Alle Erweiterungen, die nach der using-Deklaration an einem Namensbereich vorgenommen werden, sind an der Stelle, an der die Deklaration verarbeitet wird, noch nicht bekannt. Deshalb sollten alle überladenen Versionen der jeweiligen Funktion in den Namensbereich aufgenommen werden, bevor Sie die Funktion über using aufrufen.


Anonyme Namensbereiche

Beispiel

Mit der Grammatik von C++ können Sie auch anonyme Namensbereiche definieren. Verwenden Sie zu diesem Zweck das Schlüsselwort namespace, und geben Sie vor den einschließenden geschweiften Klammern keinen Bezeichner ein.

   namespace {          // Anonymer Namensbereich

      // Anweisungen

      }

Alle anonymen, unbenannten Namensbereiche im globalen Gültigkeitsbereich (d.h. unbenannte, nicht verschachtelte Namensbereiche), die zur gleichen Übersetzungseinheit gehören, verwenden einen gemeinsamen Namensbereich. Auf diese Weise können Sie statische Deklarationen auch ohne das Schlüsselwort static vornehmen.

Alle Bezeichner innerhalb eines unbenannten Namensbereichs sind innerhalb der Übersetzungseinheit, in der die unbenannten Namensbereiche definiert sind, eindeutig.


Beispiel

In der Datei ANON1.CPP

#include <iostream.h>

extern void func(void);

namespace {          // Anonym

   float pi = 3.14;  // Eindeutiger, nur in dieser Datei bekannter Bezeichner

   }

void main() {

   float pi = 0.1;

   cout << "pi = " << pi << endl;

   func();

   }

In der Datei ANON2.CPP

#include <iostream.h>

namespace {             // Anonymer Namensbereich

   float pi = 10.0001;  // Eindeutiger, nur in dieser Datei bekannter Bezeichner

   void func(void) {

       cout << "First func() called; pi = " << pi;

       }

   }

   void func(void) {

       cout << "Second func() called; pi = " << pi;

       }

Programmausgabe:

pi = 0.1

func() called; pi = 10.0001


Zugreifen auf Elemente eines Namensbereichs

Es gibt drei Möglichkeiten, auf die Elemente eines Namensbereichs zuzugreifen: durch explizite Zugriffsqualifizierung, durch die using-Deklaration und durch die Anweisung using. Ganz unabhängig von den Namensbereichen, die Sie Ihrem lokalen Gültigkeitsbereich hinzufügen, sind die Bezeichner im globalen Gültigkeitsbereich (der globale Gültigkeitsbereich ist nichts weiter als ein anderer Namensbereich) über den Zugriffsoperator :: immer zugänglich.

Explizite Zugriffsqualifizierung

Using-Anweisung

Using-Deklaration

Zugreifen auf Namensbereiche in Klassen

Beispiel

Innerhalb einer Klasse sind using-Anweisungen unzulässig. Sie können jedoch die hilfreiche using-Deklaration verwenden.


Beispiel

// Ein Beispiel für das Zugreifen auf einen Namensbereich innerhalb einer Klasse.

// Ermöglicht Ihnen, eine Funktion, die Element einer Basisklasse ist, zu überladen.

#include <iostream.h>

   class A {

   public:

      void func(char ch) { cout << "char = " << ch << endl; }

   };

   class B : public A {

   public:

//      using namespace A;   // ERROR. Die using-Anweisung ist nicht zulässig.

      void func(char *str) { cout << "string = " << str << endl; }

      // Die using-Anweisung

      using A::func;        // B::func() überladen

   };

   int main() {

      B b;

      b.func('c');  // Aufruf von A::func()

      b.func("c");  // Aufruf von B::func()

      return 0;

   }


Using-Anweisung

Beispiel

Wenn Sie einige (oder alle) Elemente eines Namensbereichs verwenden möchten, gibt es in C++ eine Möglichkeit, wie Sie ganz einfach auf vollständige Namensbereiche zugreifen können. Die using-Anweisung gibt an, daß an der Stelle, an der sie abgesetzt wird, alle Bezeichner in einem Namensbereich im Gültigkeitsbereich liegen. Die Grammatik der using-Anweisung sieht folgendermaßen aus:

   using-directive:

      using namespace  :: opt nested-name-specifier opt namespace-name;

Die using-Anweisung ist transitiv. Das bedeutet, daß Sie auch auf die Namensbereiche zugreifen können, die innerhalb des Namensbereichs liegen, auf den die Anweisung angewendet wurde. Wenn Sie beispielsweise die using-Anweisung in Ihrem Programm ausführen, können Sie auch auf die Namensbereiche A, ONE und TWO zugreifen.

   namespace A {

      using namespace ONE;  // Wurde zuvor definiert

      using namespace TWO;  // Wurde ebenfalls zuvor definiert

      }

Die using-Anweisung fügt keine Bezeichner in Ihren lokalen Gültigkeitsbereich ein. Bezeichner, die in mehreren Namensbereichen definiert sind, werden erst dann problematisch, wenn sie verwendet werden sollen. Deklarationen, die den lokalen Gültigkeitsbereich betreffen, haben Vorrang. Alle anderen ähnlichen Deklarationen werden verborgen.


Beispiel

// Ein Beispiel für die using-Anweisung

#include <iostream.h>

   namespace F {

      float x = 9;

      }

   namespace G {

      using namespace F;

      float y = 2.0;

         namespace INNER_G {

            float z = 10.01;

            }

      }

   int main() {

      using namespace G;  // DIESE ANWEISUNG GIBT IHNEN ALLES, WAS IN "G" DEKLARIERT IST

      using namespace G::INNER_G;  // DIESE ANWEISUNG GIBT IHNEN NUR "INNER_G"

      float x = 19.1;     // LOKALE ANWEISUNG HAT VORRANG

      cout << "x = " << x << endl;

      cout << "y = " << y << endl;

      cout << "z = " << z << endl;

      return 0;

      }

Ausgabe:

   x = 19.1

   y = 2

   z = 10.01


Using-Deklaration

Beispiel

Mit der Syntax der using-Deklaration können Sie einzeln auf Elemente des Namensbereichs zugreifen. Durch die Deklaration wird der deklarierte Bezeichner in den lokalen Namensbereich eingefügt. Grammatik:

   using-Deklaration:

          using :: unqualifizierter-Bezeichner;


Beispiel

// Ein Beispiel für die using-Deklaration.

// Die Funktion g() wird in zwei verschiedenen Namensbereichen definiert.

#include <iostream.h>

namespace ALPHA {  /* ALPHA ist der Name dieses Namensbereichs. */

   float f(float y) { return y; }

   void g() { cout << "ALPHA version" << endl; }

   }

namespace BETA {  /* BETA ist der Name dieses Namensbereichs. */

      void g() { cout << "BETA version" << endl; }

         }

void main(void) {

// Die using-Deklaration kennzeichnet die gewünschte Version von g ().

         using ALPHA::f;  // Qualifizierte Deklaration

         using BETA::g;   // Qualifizierte Deklaration

         float x = 0;

         // Zugriffs-Qualifizierer werden nicht mehr benötigt.

         x = f(2.1);

         g();

         }


Explizite Zugriffsqualifizierung

Sie können die einzelnen Elemente eines Namensbereichs explizit qualifizieren. Geben Sie dafür den Bezeichner des Namensbereichs und den Zugriffsoperator :: gefolgt vom Namen des Elements ein. Wenn Sie z.B. auf ein bestimmtes Element des Namensbereichs ALPHA zugreifen möchten, müssen Sie folgendes eingeben:

      ALPHA::LD;  // Greift auf eine Variable zu

      ALPHA::f;   // Greift auf eine Funktion zu

Explizite Zugriffsqualifizierung vermeidet Mehrdeutigkeiten. Mit dem Zugriffsoperator :: können Sie auf die Bezeichner in beliebigen Namensbereichen zugreifen (auch Namensbereiche, die im lokalen Gültigkeitsbereich bereits in Verwendung sind). Es ist dabei unerheblich, welche Namensbereiche in Ihrem Subsystem verwendet werden (allerdings keine anonymen Namensbereiche). Jeder Bezeichner in der Anwendung ist daher zugänglich, wenn er ausreichend qualifiziert wird.

Neue Typumwandlung

Dieser Abschnitt beschreibt alternative Methoden Typumwandlung. Die hier vorgestellten Methoden erweitern die alten Umwandlungsausdrücke (die immer noch verfügbar sind) der Sprache C.

Typen können in einer Umwandlung nicht definiert werden.


Neue Typumwandlung

Siehe auch

Dieser Abschnitt beschreibt alternative Methoden Typumwandlung. Die hier vorgestellten Methoden erweitern die alten Umwandlungsausdrücke (die immer noch verfügbar sind) der Sprache C.

Typen können in einer Umwandlung nicht definiert werden.


const_cast (Typumwandlungs-Operator)
const_cast

Siehe auch

Syntax

const_cast< T > (arg)

Beschreibung

Der Operator const_cast dient dazu, die Modifizierer const und volatile von einem Typ zu "entfernen".

In dem Ausdruck const_cast< T > (arg) müssen T und arg vom gleichen Typ sein und dürfen sich höchstens durch die Modifizierer const und volatile unterscheiden. Die Typumwandlung findet beim Compilieren statt. Das Ergebnis hat den Typ T. Mit einem einzigen const_cast-Ausdruck können beliebig viele const- oder volatile-Modifizierer hinzugefügt oder entfernt werden.

Ein Zeiger auf einen const-Typ kann in einen Zeiger auf einen nicht-const-Typ umgewandelt werden, wobei letzterer in jeder anderen Hinsicht mit dem ursprünglichen identisch ist. Wenn die Umwandlung durchgeführt werden kann, zeigt der sich dabei ergebende Zeiger auf das ursprüngliche Objekt.

Aus einem Objekt const oder einer Referenz auf ein Objekt const wird ein nicht-const-Objekt bzw. eine nicht-const-Referenz.

Der Operator const_cast bewirkt eine ähnliche Typumwandlung auch in Bezug auf den Modifizierer volatile. Ein Zeiger auf ein Objekt volatile kann in einen Zeiger auf ein nicht-volatile-Objekt umgewandelt werden, ohne den Typ des Objekts in anderer Weise zu verändern. Das Ergebnis ist ein Zeiger auf das ursprüngliche Objekt. Aus einem Objekt volatile oder einer Referenz auf ein Objekt volatile kann ein ansonsten identisches nicht-volatile-Objekt bzw. eine ansonsten identische nicht-volatile-Referenz gemacht werden.


dynamic_cast (Typumwandlungs-Operator)
dynamic_cast

Siehe auch	Beispiel

In dem Ausdruck dynamic_cast< T > (ptr) muß T ein Zeiger oder eine Referenz auf  einen definierten Klassentyp oder auf void sein. Das Argument ptr muß ein Ausdruck sein, dessen Auswertung einen Zeiger oder eine Referenz ergibt.

Wenn T ein Zeiger auf void ist, muß ptr ebenfalls ein Zeiger sein. In diesem Fall kann man über den Zeiger auf jedes Element derjenigen Klasse zugreifen, die in der Klassenhierarchie die unterste ist. Eine solche Klasse kann nicht Basisklasse irgendeiner anderen Klasse sein.

Die Umwandlung von einer abgeleiteten Klasse in eine Basisklasse oder in eine andere abgeleitete Klasse geschieht wie folgt: Wenn T ein Zeiger ist und ptr ein Zeiger auf eine Nicht-Basisklasse, die ein Element einer Klassenhierarchie ist, so ist das Ergebnis ein Zeiger auf die eindeutige Unterklasse. Referenzen werden ähnlich behandelt. Wenn T eine Referenz ist und ptr eine Referenz auf eine Nicht-Basisklasse, so ist das Ergebnis eine Referenz auf die eindeutige Unterklasse.

Eine Umwandlung von einer Basisklasse in eine abgeleiteten Klasse ist nur möglich, wenn die Basisklasse von polymorphem Typ ist.

Eine Umwandlung in eine Basisklasse wird während der Compilierung durchgeführt. Eine Umwandlung einer Basisklasse in eine abgeleitete Klasse oder eine Umwandlung quer durch die Hierarchie erfolgt dagegen zur Laufzeit.

Wenn die Umwandlung durchgeführt werden kann, so wandelt der Ausdruck dynamic_cast< T > (ptr) den Zeiger ptr in den gewünschten Typ um. Falls die Umwandlung eines Zeigertyps nicht möglich ist, hat der zurückgegebene Zeiger den Wert NULL. Falls die Umwandlung eines Referenztyps nicht möglich ist, so wird die Exception Bad_cast exception ausgelöst.

Hinweis:	Für die Nutzung von dynamic_cast müssen Laufzeitinformationen (RTTI) verfügbar gemacht werden.


dynamic_cast (Beispiel)

// WIE MAN MIT DYNAMISCHEN TYPUMWANDLUNGEN (DYNAMIC_CAST) ARBEITET.

// Dieses Programm muß mit der Option -RT (RTTI erzeugen) compiliert
 werden.

#include <iostream.h>

#include <typeinfo.h>

class Basis1

{

   // Damit der RTTI-Mechanismus korrekt funktioniert,

   // muß die Basisklasse polymorph sein.

   virtual void f(void) { /* Eine virtuelle Funktion macht die Klasse
 polymorph. */ }

};

class Basis2 { };

class Abgeleitet : public Basis1, public Basis2 { };

int main(void) {

   try {

      Abgeleitet d, *pd;

      Basis1 *b1 = &d;

      // Objekt Basis1 nach Abgeleitet umwandeln.

      if ((pd = dynamic_cast<Abgeleitet *>(b1)) != 0) {

           cout << "Der sich ergebende Zeiger hat den Typ "

                << typeid(pd).name() << endl;

         }

      else throw Bad_cast();

      // Eine Typumwandlung quer durch die Hierarchie versuchen, in

      // diesem Fall eine Umwandlung von der ersten Basisklasse zur

      // untersten abgeleiteten Klasse und dann zurück zu einer
    // anderen Basisklasse

      Basis2 *b2;

      if ((b2 = dynamic_cast<Basis2 *>(b1)) != 0) {

          cout << "Der sich ergebende Zeiger hat den Typ "

               << typeid(b2).name() << endl;

         }

      else throw Bad_cast();

      }

   catch (Bad_cast) {

      cout << "dynamic_cast hat versagt" << endl;

      return 1;

      }

   catch (...) {

      cout << "Fehler bei der Exception-Behandlung." << endl;

      return 1;

      }

   return 0;

}


reinterpret_cast (Typumwandlungs-Operator)
reinterpret_cast

Siehe auch	Beispiel

Syntax

reinterpret_cast< T > (arg)

Beschreibung

Im Ausdruck reinterpret_cast< T > (arg) muß T ein Zeiger, eine Referenz, ein arithmetischer Typ, ein Zeiger auf eine Funktion oder ein Zeiger auf ein Element sein.

Ein Zeiger kann explizit in einen Ganzzahltyp umgewandelt werden.

Ist arg eine Ganzzahl, so kann sie in einen Zeiger umgewandelt werden. Bei der Umwandlung eines Zeigers in einen Ganzzahltyp und wieder zurück in denselben Zeigertyp erhält man den ursprünglichen Wert.

Bei der Umwandlung eines Zeigers oder einer Referenz kann eine noch undefinierte Klasse benutzt werden.

Ein Zeiger auf eine Funktion kann explizit in einen Zeiger auf ein Objekt umgewandelt werden, vorausgesetzt, der Typ des Objektzeigers ist (vom Speicherplatzbedarf her) groß genug, um einen Funktionszeiger aufzunehmen. Ein Zeiger auf einen Objekttyp kann explizit in einen Zeiger auf eine Funktion umgewandelt werden, vorausgesetzt, der Typ des Funktionszeigers ist groß genug, um den Objektzeiger aufzunehmen.


// reinterpret_cast Beispiel

// Verwenden Sie die Umwandlung "reinterpret_cast<Typ>(ausdruck)"

// als Ersatz für die Umwandlung "(Typ)ausdruck" in Fällen, in denen

// die Umwandlung nicht ganz sicher oder impementierungsabhängig ist.

void funk(void *v) {

        // Umwandlung eines Zeigertyps in einen Ganzzahl-Typ.

        int i = reinterpret_cast<int>(v);

  .

  .

  .

}

void main() {

   // Umwandlung eines Ganzzahl-Typs in einen Zeigertyp.

   funk(reinterpret_cast<void *>(5));

   // Umwandlung eines Zeigers auf eine Funktion eines bestimmten Typs

   // in einen Zeiger auf eine Funktion eines anderen Typs.

   typedef void (* PFV)();

   PFV pfunk = reinterpret_cast<PFV>(funk);

   pfunk();

   }


static_cast (Typumwandlungs-Operator)
static_cast

Siehe auch

Syntax

static_cast< T > (arg)

Beschreibung

In dem Ausdruck static_cast< T > (arg) muß T ein Zeiger, eine Referenz, ein arithmetischer Typ oder ein enum-Typ sein. Der Typ von arg muß zu dem von T passen. Sowohl arg als auch T müssen zur Compilierzeit vollständig bekannt sein.

In allen Fällen, in denen ein vollständiger Typ mit den sonstigen herkömmlichen Mitteln der Programmiersprache in einen anderen Typ umgewandelt werden kann, führt die Umwandlung mit dem Operator static_cast exakt zum selben Ergebnis.

Ganzzahltypen können in Aufzählungstypen umgewandelt werden. Der Versuch, arg in einen Wert umzuwandeln, der keiner der Aufzählungskonstanten entspricht, liefert ein undefiniertes Ergebnis.

Ein Nullzeiger wird in einen Nullzeiger umgewandelt.

Ein Zeiger auf ein Objekt eines bestimmten Typs kann in einen Zeiger auf ein Objekt eines anderen Typs umgewandelt werden. Es ist zu beachten, daß allein schon das Zeigen auf ähnliche Typen Zugriffsprobleme verursachen kann, falls diese ähnlichen Typen nicht in gleicher Weise im Speicher ausgerichtet sind.

Ein Zeiger auf eine Klasse X kann explizit in einen Zeiger auf eine Klasse Y umgewandelt werden, falls X eine Basisklasse für Y ist. Eine statische Umwandlung ist nur unter folgenden Bedingungen möglich:

Es gibt nur eine eindeutige Umwandlungsmöglichkeit von Y nach X.

X ist keine virtuelle Basisklasse.

Ein Objekt kann explizit in den Referenztyp X& umgewandelt werden, falls ein Zeiger auf dieses Objekt explizit in X* umgewandelt werden kann. Das Ergebnis der Umwandlung ist ein L-Wert. Bei der Umwandlung in eine Referenz werden keine Konstruktoren oder Umwandlungsfuktionen aufgerufen.

Ein Objekt oder ein Wert kann nur dann in ein Klassenobjekt umgewandelt werden, wenn ein passender Konstruktor oder Umwandlungsoperator deklariert worden ist.

Ein Zeiger auf ein Element kann nur dann explizit in einen anderen Elementzeiger-Typ umgewandelt werden, wenn beide Typen Zeiger auf Elemente derselben Klasse oder Zeiger auf Elemente zweier Klassen, von denen die eine ohne Mehrdeutigkeit von der anderen abgeleitet ist, sind.

Wenn T eine Referenz ist, so ist das Ergebnis von static_cast< T > (arg) ein L-Wert. Das Ergebnis einer Zeiger- oder Referenzumwandlung weist auf den ursprünglichen Ausdruck.


Übersicht Typidentifikation zur Laufzeit
Übersicht Typidentifikation zur Laufzeit (Run-time type identification, RTTI)

Siehe auch

Typidentifikation zur Laufzeit (Run-time type identification, RTTI) ermöglicht Ihnen das Schreiben von portablem Quelltext, den den aktuellen Typ eines Datenobjektes zur Laufzeit bestimmen kann. Das ist auch dann möglich, wenn der Quelltext sich nur auf einen Zeiger oder eine Referenz des Objektes bezieht. Das macht es beispielsweise möglich, einen Zeiger auf eine virtuelle Basisklasse in einen Zeiger auf den abgeleiteten Typ des aktuellen Objektes zu konvertieren. Verwenden Sie den Operator dynamic_cast, um Umwandlungen zur Laufzeit durchzuführen.

Der Mechanismus von RTTI läßt Sie auch überprüfen, ob ein Objekt von einem besonderen Typ ist und ob zwei Objekte von gleichen Typ sind. Sie können das mit dem Operator typeid machen, der den aktuellen Typ seines Argumentes bestimmt und eine Referenz auf ein Objekt des Typs const typeinfo liefert, das diesen Typ beschreibt.

Sie können auch einen Typnamen als Argument von typeid verwenden. typeid wird dann eine Referenz auf ein konstantes Objekt const typeinfo dieses Typs liefern. Die Klasse typeinfo stellt einen operator== und einen operator!= zu Verfügung, die Sie verwenden können, um zu bestimmen ob zwei Objekte vom gleichen Typ sind.. Die Klasse typeinfo stellt auch einen Elementfunktionsnamen zur Verfügung, der einen Zeiger auf eine Zeichenkette, die den Namen des Typs enthält, liefert.


__rtti (Beispiel)

/* WIE MAN LAUFZEIT-TYPINFORMATIONEN FÜR POLYMORPHE KLASSEN ERHÄLT.*/

#include <iostream.h>

#include <typeinfo.h>

class __rtti Alpha {      /* RTTI für diese Klasse und für alle von */

                           /* ihr abgeleiteten Klassen verfügbar machen.*/

    virtual void funk() {};  /* Eine virtuelle Funktion macht Alpha */

                             /* zu einer polymorphen Klasse. */

};

class B : public Alpha {};

int main(void) {

   B Binst;           // Instanz der Klasse B erzeugen

   B *Bptr;           // Zeiger vom Typ "Zeiger auf B" deklarieren

   Bptr = &Binst;     // Den Zeiger initialisieren

   // FOLGENDE PRÜFUNGEN FINDEN ZUR LAUFZEIT STATT

   try {

      if (typeid( *Bptr ) == typeid( B ) )

          // Frage: "WELCHEN TYP HAT *Bptr?"

          cout << "Der Name ist " << typeid( *Bptr).name();

      if (typeid( *Bptr ) != typeid( Alpha ) )

          cout << "\nZeiger ist nicht vom Typ Zeiger auf Aplpha";

      return 0;

      }

   catch (Bad_typeid) {

      cout << "typeid() nicht erfolgreich.";

      return 1;

      }

   }

Ausgabe des Programms:

Der Name ist B

Zeiger ist nicht vom Typ Zeiger auf Aplpha


Operator typeid

Siehe auch	Beispiel	Schlüsselwort

Syntax

typeid( ausdruck )

typeid( typ_name )

Beschreibung

Verwenden Sie das Schlüsselwort typeid, um zur Laufzeit Identifizierungsinformationen über Typen und Ausdrücke verfügbar zu machen. Der typeid-Aufruf liefert eine Referenz auf ein Objekt des Typs const typeinfo zurück. Dieses Objekt repräsentiert den Typ des typeid-Operanden.

Wenn der Operand von typeid ein dereferenzierter Zeiger oder eine Referenz auf einen polymorphen Typ ist, so liefert typeid den dynamischen Typ des Objekts, auf das der Zeiger bzw. die Referenz im Moment weist, zurück. Wenn der Operand nicht polymorph ist, liefert typeid ein Objekt zurück, das den statischen Typ repräsentiert.

Der Operator typeid kann sowohl mit den eingebauten Basisdatentypen als auch mit anwenderdefinierten Datentypen zusammen benutzt werden.

Wenn der Operand von typeid ein dereferenzierter Nullzeiger ist, so wird die Exception Bad_typeid ausgelöst.


__rtti
__rtti und die Option -RT

Siehe auch	Beispiel	Schlüsselwort

Bei C++Builder ist RTTI in der Voreinstellung aktiviert. Verwenden Sie die Kommandozeilenoption RT, um RTTI zu deaktivieren (-RT-) oder zu aktivieren (-RT). Wenn RTTI nicht aktiviert ist oder wenn das Argument von typeid ein Zeiger oder eine Referenz auf eine nichtpolymorphe Klasse ist, so liefert typeid eine Referenz auf ein Objekt vom Typ const typeinfo zurück, das den deklarierten Typ des Zeigers bzw. der Referenz selbst beschreibt, aber nicht den des aktuellen Objektes, auf das der Zeiger bzw. die Referenz weist.

Außerdem können Sie sogar dann, wenn RTTI nicht aktiviert ist, bestimmen, daß alle Instanzen einer bestimmten Klasse und aller von dieser abgeleiteten Klassen polymorphe Laufzeit-Typinformationen (soweit sinnvoll) liefern, indem Sie das C++Builder Schlüsselwort __rtti in der Klassendefinition benutzen.

Wenn Sie die Compiler-Option -RT- benutzen und dabei irgendeine Basisklasse mit __rtti deklariert ist, so müssen alle polymorphen Basisklassen ebenfalls mit __rtti deklariert sein.

struct __rtti S1 { virtual s1funk(); };  /* Polymorph */

struct __rtti S2 { virtual s2funk(); };  /* Polymorph */

struct X : S1, S2 { };

Wenn Sie den RTTI-Mechanismus deaktivieren (mit Hilfe der Compiler-Option -RT-), so sind für abgeleitete Klassen möglicherweise keine RTTI-Informationen verfügbar. Wenn eine Klasse von mehreren Klassen abgeleitet ist, so hängt es von Reihenfolge und Typ der Basisklassen ab, ob die Klasse die RTTI-Möglichkeit erbt oder nicht.

Wenn Sie gleichzeitig mit polymorphen und nicht-polymorphen Klassen arbeiten, wird die Reihenfolge der Vererbung wichtig. Wenn Sie die folgenden Deklarationen mit -RT- compilieren, sollten Sie X mit dem Modifizierer __rtti deklarieren. Versäumen Sie dies, so führt eine Veränderung der Reihenfolge der Basisklassen von X zu der Compiler-Fehlermeldung Can't inherit non-RTTI class from RTTI base 'S1' (Kann keine Nicht-RTTI-Klassen von der RTTI-Basis "S1" erben).

struct __rtti S1 { virtual funk(); };   /* Polymorphe Klasse */

struct S2 { };                           /* Nichtpolymorphe Klasse */

struct __rtti X : S1, S2 { };

Hinweis:	Die Klasse X wird explizit mit __rtti deklariert. Dadurch wird sichergestellt, daß Reihenfolge und Typ der Klassen problemlos verändert werden können.

Im folgenden Beispiel erbt die Klasse X ausschließlich von nicht-polymorphen Klassen; sie muß deshalb nicht mit __rtti deklariert werden.

struct __rtti S1 {  };   // Nichtpolymorphe Klasse

struct S2 { };

struct X : S2, S1 { };   // Die Reihenfolge ist ohne Bedeutung

Die Verwendung des Schlüsselworts __rtti oder der Compiler-Option -RT macht eine statische Klasse nicht zu einer polymorphen Klasse.


Option -RT und Destruktoren

Wenn -xd aktiviert ist, kann ein Zeiger auf eine Klasse mit einem virtuellen Destruktor nicht gelöscht werden, wenn diese Klasse nicht mit -RT compiliert wurde. Die Optionen -RT und -xd sind in der Voreinstellung angeschaltet.

Beispiel

class Alpha {

public:

   virtual ~Alpha( ) { }

};

void func( Alpha *Aptr ) {

   delete Aptr;         // Fehler. Alpha ist kein polymorpher Klassentyp

   }


Referenzen

Siehe auch

Während in C Argumente nur als Wert übergeben werden können, ist es in C++ möglich, Argumente als Wert oder als Referenz  zu übergeben. Die C++ Referenztypen sind den Zeigertypen sehr ähnlich. Sie erzeugen Aliasnamen für Objekte, die zur Übergabe von Argumenten an Funktionen mit Referenzparametern benutzt werden können. In den folgenden Themen finden Sie eine Diskussion der Referenzierung.

Einfache Referenzen

Referenzen als Argumente

Indirekte Referenzoperatoren

Hinweis:	C++ spezifische Zeigerreferenzierung und -dereferenzierung wird unter C++ spezifische Operatoren vertieft.


Einfache Referenzen

Siehe auch

Der Referenzdeklarator kann dazu benutzt werden, Referenzen außerhalb von Funktionen zu definieren:

int  i  = 0;

int &ir = i;   // ir ist ein Alias für i

ir = 2;        // dieselbe Bedeutung wie i = 2

Hinweis:	type& var, type &var und type & var sind gleichbedeutend.

Im Beispiel wird der L-Wert ir als Alias für i deklariert, wobei der Initialisierer vom gleichen Typ wie die Referenz sein muß. Alle Operationen auf ir haben denselben Effekt wie Operationen auf i. So weist ir = 2 beispielsweise i den Wert 2 zu, und &ir liefert die Adresse von i zurück.


Referenzen als Argumente

Siehe auch

Der Referenzdeklarator kann auch dazu benutzt werden, um Referenzen als Übergabeparameter einer Funktion zu definieren:

void func1 (int i);

void func2 (int &ir);    // ir ist vom Typ "Referenz auf int"

    .

    .

    .

int sum=3;

func1(sum);              // sum wird als Wert übergeben

func2(&sum);             // sum wird als Referenz übergeben

Das als Referenz übergebene Argument sum kann von der Funktion func2 direkt geändert werden. Dagegen erhält func1 nur eine Kopie des Werts von sum und kann so sum selbst nicht ändern.

Wird ein Argument x als Wert übergeben, erhält der formale Parameter innerhalb der Funktion eine Kopie des Werts von x. Eine Änderung dieser Kopie innerhalb der aufgerufenen Funktion hat keine Auswirkung auf den Wert von x selbst. Natürlich kann die Funktion einen Wert zurückgeben, der später zur Änderung von x benutzt werden kann. Die Funktion kann die Änderung allerdings nicht selbst durchführen.

Die traditionelle C-Methode zum Ändern von x verwendet das Argument &x, die Adresse von x, anstelle von x. Obwohl &x als Wert übergeben wird, kann die Funktion auf x über die erhaltene Kopie von &x zugreifen. Selbst wenn x von der Funktion nicht geändert werden muß, ist es oft nützlich, &x zu übergeben (es kann dadurch allerdings zu unerwünschten Nebenwirkungen kommen). Dies gilt insbesondere dann, wenn x eine große Datenstruktur ist. Indem x direkt als Wert übergeben wird, spart man sich das speicherfressende Kopieren der Datenstruktur.

Vergleichen Sie die drei folgenden Implementierungen der Funktion treble:

Implementierung 1

int treble_1(int n)

{

   return 3 * n;

}

  .

  .

  .

int x, i = 4;

x = treble_1(i);         // x jetzt = 12, i = 4

  .

  .

  .

Implementierung 2

void treble_2(int* np)

{

   *np = (*np) * 3;

}

  .

  .

  .

treble_2(int& i);        // i jetzt = 12

Implementierung 3

void treble_3(int& n)    // n ist ein Referenztyp

{

   n = 3 * n;

}

  .

  .

  .

treble_3(i);             // i jetzt = 36

Die formale Argumentdeklaration type& t (entspricht type& t) definiert t als Typ "Referenz auf type". Wird demnach treble_3 mit dem realen Argument i aufgerufen, dient i zur Initialisierung des formalen Referenzarguments n. n agiert somit als Alias für i, so daß die Zuweisung n = 3*n gleichbedeutend mit der Zuweisung  3 * i zu i ist.

Ist der Initialisierer eine Konstante oder ein Objekt mit einem anderen Typ als der Referenztyp, dann wird ein temporäres Objekt erzeugt, für das die Referenz als Alias agiert:

int& ir = 6;   /* erzeugt temporäres Integerobjekt, ir als Alias, erhält Wert 6 */

float f;

int& ir2 = f;  /* erzeugt temporäres Integerobjekt mit ir2 als Alias, f wird

                  vor der Zuweisung konvertiert */

ir2 = 2.0      // ir2 erhält nun den Wert 2, f bleibt unverändert

Die automatische Erzeugung von temporären Objekten ermöglicht die Konvertierung von Referenztypen, wenn die formalen und tatsächlichen Argumente verschiedene (aber zuweisungskompatible) Typen haben. Bei der Wertübergabe gibt es allerdings weniger Konvertierungsprobleme, da die Kopie des aktuellen Arguments physikalisch geändert werden kann, bevor sie dem formalen Argument zugewiesen wird.


Zuweisungsbereichsoperator ::
Zugriffsbereichsoperator ::

Der Zugriffsoperator (oder Zugriffsbereichsoperator) :: (Zwei Doppelpunkte) läßt Sie auf einen globalen (oder im Bereich Datei gültigen) Elementnamen auch dann zugreifen, wenn er durch eine lokale Redeklaration dieses Names versteckt wurde. Sie können einen globalen Identifizierer verwenden, indem Sie ihn einem Zuweisungsoperator voranstellen. Um auf einen versteckten Elementnamen zuzugreifen, spezifizieren Sie den Klassennamen und verwenden Sie den Zuweisungsoperator. Deshalb sind Alpha::func( ) und Beta::func( ) zwei verschiedene Funktionen.


::new, Operator
Operator new

Siehe auch	Beispiel	Operatoren

Syntax

<::> new <neue_argumente> typname <(initialisierer)>

<::> new <neue_argumente> (typname) <(initialisierer)>

Beschreibung

Der Operator new ermöglicht eine dynamische Speicherreservierung, ähnlich der Standardbibliotheksfunktion malloc, aber leistungsfähiger Der Operator new erfordert stets die Angabe eines Datentyps für typname. Die in spitze Klammern eingeschlossenen Elemente sind optional und sie haben folgende Bedeutung:

::  Operator, ruft die globale Version von new auf.

neue_argumente steht für zusätzliche Argumente für new. Diese Syntaxform können Sie nur verwenden, wenn es eine überladene Version von new gibt, zu der diese optionalen Argumente passen. Siehe auch die Diskussion der neue_argumente Syntax.

initialisierer dient (wenn angegeben) zur Initialisierung des reservierten Speicherbereichs.

Eine Speicheranforderung für ein Objekt, das kein Array ist, führt zum Aufruf der entsprechenden Operatorfunktion new(). Jede Speicheranforderung für ein Array führt zum Aufruf der entsprechenden Operatorfunktion new[](). Der Operator wird wie folgt ausgewählt:

Zuweisen von Arrays vom Typ Type:

1 Versucht eine klassenspezifische Speicherzuteilung für Arrays:

   Type::operator new[]()

2 Wenn die klassenspezifische Speicherzuteilung für Arrays nicht definiert ist, wird die globale Version verwendet:

   ::operator new[]()

Reservierung beim Typ Type, wenn es sich im keine Arrays handelt:

1 Versucht eine klassenspezifische Speicherzuteilung zu verwenden:

   Type::operator new()

2 Wenn die klassenspezifische Speicherzuteilung nicht definiert ist, wird die globale Version verwendet:

   ::operator new()

Reservierung von einzelnen Objekten (die nicht vom Typ Klasse sind), die sich nicht in Arrays befinden:

1 Bei einer Speicherreservierung für Objekte, die keinen Arrays sind, wird  der ::operator new() verwendet. Beachten Sie, daß diese Reservierungsfunktion nicht immer für die vordefinierten Typen verwendet wird. Es ist möglich, diese globale Oparatorfunktion zu überladen. Das wird jedoch nicht allgemein empfohlen.

Reservierung von Speicherplatz für Arrays:

1 Verwenden Sie den globalen Operator für die Reservierung:

   ::operator new[] ()

Hinweis:	Arrays von Klassen erfordern den Standardkonstruktor.

Der Operator new versucht, ein Objekt des Typs Typ zu erzeugen, indem er (falls möglich) sizeof(Typ) Bytes im freien Speicherbereich (auch Heap genannt) reserviert. Dabei berechnet new die Größe von Typ, ohne einen expliziten sizeof-Operator zu benötigen. Außerdem ist der zurückgegebene Zeiger vom passenden Typ "Zeiger auf Typ", so daß keine explizite Typumwandlung erforderlich ist. Die Lebensdauer eines Objekts new erstreckt sich von seiner Erzeugung bis zu seiner Zerstörung durch den Operator delete, der die Speicherreservierung löscht, oder bis zum Programmende.

War die Reservierung erfolgreich, so gibt new einen Zeiger auf das neue Objekt zurück. In der Voreinstellung führt das Fehlschlagen (z.B. wegen zu wenig oder zu fragmentarischem Heap-Speicher) zu der vordefinierten Exception xalloc. Ihr Programm sollte immer mit catch auf die Exception xalloc reagieren und nicht einfach blind auf das neue Objekt zugreifen (es sei denn, Sie verwenden einen new-Handler).

Auf eine Anforderung nach Reservierung von null Bytes wird ein Zeiger ungleich NULL zurückgegeben. Wiederholte Anforderungen nach null Bytes ergeben jeweils unterschiedliche Zeiger ungleich NULL.


delete
Operator delete

Siehe auch	Beispiel	Operatoren

Syntax

<::> delete cast_ausdruck

<::> delete [ ] cast_ausdruck

delete <array_name> [];

Beschreibung

Der Operator delete dient zum Löschen dynamischer Speicherreservierungen; er gibt einen Speicherblock frei, der durch einen vorhergehenden Aufruf von new. reserviert wurde. Dieser Operator arbeitet ähnlich wie die Standardbibliotheksfunktion free., ist aber leistungsfähiger.


Plazierungssyntax für den Operator new

Beispiel

Die Plazierungssyntax für den operator new( ) kann nur verwendet werden, wenn sie den Operator für die Allokation mit passenden Argumenten überladen haben. Sie können die Plazierungssyntax benutzen, wenn Sie Speicherplatz verwenden und wiederholt verwenden wollen, den Sie einmal am Anfang Ihres Programmes aufgebaut haben.

Wenn Sie den überladenen operator new( ) verwenden, um zu spezifizieren wo Sie eine Allokation plazieren wollen, sind Sie selbst für das Freigeben der Allokation verantwortlich. Weil Sie Ihre Version des Allokationsoperators aufrufen, hängt das Aufräumen nicht von dem globalen ::operator delete( ) ab.

Um Speicher freizugeben, rufen Sie den Destruktor explizit auf. Von dieser Methode zum Aufräumen von Speicher sollte nur in speziellen Situationen und mit großer Vorsicht verwendet werden. Wenn Sie einen expliziten Aufruf eines Destruktors machen, bevor ein bereits konstruiertes Objekt auf dem Stack den Bereich verläßt, den wird der Destruktor wieder aufgerufen wenn der Stack-Frame aufgeräumt ist.


Fehlerbehandlung für den Operator new

Siehe auch

Sie können eine Funktion definieren, die aufgerufen wird wenn der Operator new nicht erfolgreich ist. Um dem Operator new über die new-Handler-Funktion zu informieren, können Sie set_new_handler verwenden und einen Zeiger an new-Handler übergeben. Wenn new eine Null oder einen Fehler zurückliefern soll, müssen Sie set_new_handler(0) verwenden.


Der Operator new für Arrays

Beispiel

Ist das Objekt, auf das new angewendet wird, ein Array, so liefert der Operator new[]() einen Zeiger auf das erste Element des Arrays zurück. Soll mit new ein mehrdimensionales Array erzeugt werden, so müssen alle Dimensionen des Arrays wie folgt angegeben werden (die am weitesten links stehende muß aber keine zur Compilierzeit bekannte Konstante sein):

mat_ptr = new int[3][10][12];    // OK

mat_ptr = new int[n][10][12];    // OK

mat_ptr = new int[3][][12];      // Unzulässig

mat_ptr = new int[][10][12];     // Unzulässig

Obwohl die erste Array-Dimension eine Variable sein kann, müssen alle folgenden Dimensionen Konstanten sein.


Der Operator delete für Arrays

Der Operator delete[]() dient zum Löschen von Arrays, dabei muß die Syntax delete [] expr benutzt werden. Bei der C++ Version 2.1 und später sollte in den eckigen Klammern nicht die Array-Dimension angegeben werden:

char * p;

void func()

{

   p = new char[10];    // Platz für 10 Zeichen (Typ char)

   delete[] p;          // Löschen der 10 Zeichen mit delete

}

In der C++ Version 2.0 war die Angabe der Array-Größe erforderlich. Um auch 2.0-Programme übersetzen zu können, gibt C++Builder ggfs. eine Warnung aus und ignoriert einfach jede Größenangabe. Wenn im vorigen Beispiel also delete[10] p; stehen würde, so würde bei der Compilierung folgende Warnung erscheinen:

Warning: Array size for 'delete' ignored in function func()


::new, Operator
Operator new

Siehe auch

Standardmäßig gibt es keine überladene Version von new, jede Anforderung nach dynamischer Speicherreservierung läuft über die globale Version von new, also über ::operator new(). Eine Anforderung zur Reservierung eines Array ruft ::operator new[]() auf. Für Objekte einer Klasse vom Typ name kann ein spezieller Operator definiert werden, der mit name::operator new() oder name::operator new[]() aufgerufen wird. Bei der Anwendung von new auf Objekte der Klasse name wird der entsprechende Operator, also name::operator new aufgerufen, falls er existiert; andernfalls wird der globale Operator ::operator new verwendet.

Nur die Funktion operator new() kennt einen optionalen Initialisierer. Die Version zur Speicherreservierung für Arrays, operator new[](), akzeptiert keinen Initialisierer. Wenn keine Initialisierer explizit angegeben sind, so enthalten die durch new erzeugten Objekte unvorhersagbare Zufallswerte. Durch new erzeugte Objekte könne aber, falls es keine Arrays sind, mit einem passenden, in runden Klammern stehenden Ausdruck initialisiert werden:

int_ptr = new int(3);

Arrays in Klassen mit Konstruktoren werden vom Standardkonstruktor initialisiert. Der anwenderdefinierte Operator new mit anwenderdefinierter Initialisierung spielt bei C++ Konstruktoren für Klassenobjekte eine sehr wichtige Rolle.


Überladen des Operators new

Beispiel

Die globalen Operatoren ::operator new() und ::operator new[]() können überladen werden. Jede überladene Instanz muß einen eigenen, eindeutigen Funktionskopf haben. Damit können dann mehrere Instanzen eines globalen Speicherreservierungsoperators im selben Programm existieren.

Klassenspezifische new-Operatoren können ebenfalls überladen werden. Die neue Operatorfunktion new kann implementiert werden, um eine Speicherreservierung (Heap) mit einer anderen Strategie zur Verfügung zu stellen oder um zusätzliche Argumente aufzunehmen. Ein anwenderdefinierter new-Operator muß den Typ void* zurückliefern und sein erstes Argument muß vom Typ size_t sein. Zum Überladen der new-Operatoren verwendet man die folgenden Prototypen:

void * operator new(size_t Typ_groesse);    // Für Nicht-Arrays

void * operator new[](size_t Typ_groesse);  // Für Arrays

Der C++Builder Compiler stellt Typ_groesse dem new-Operator zur Verfügung. Für Typ kann jeder Datentyp eingesetzt werden, mit Ausnahme von Funktionen (Zeiger auf Funktionen sind aber erlaubt), Klassendeklarationen, Aufzählungstypdeklarationen, const und volatile.


Überladen des Operators delete

Beispiel

Die globalen Operatoren ::operator delete() und ::operator delete[]() können nicht überladen werden. Sie können jedoch die Standardversion dieser Operatoren durch Ihre eigene Implementierung ersetzen. Im Programm kann von jedem dieser Operatoren nur eine einzige Instanz existieren.

Ein anwenderdefinierter delete-Operator muß den Typ void zurückliefern und sein erstes Argument muß vom Typ void * sein. Ein zweites Argument vom Typ size_t ist optional. In einer Klasse T kann maximal eine Version jedes der Operatoren T::operator delete[]() und T::operator delete() definiert werden. Zum Überladen der delete-Operatoren verwendet man die folgenden Prototypen:

void operator delete(void *Typ_ptr, [size_t Typ_groesse]);     // Für Nicht-Arrays

void operator delete[](size_t Typ_ptr, [size_t Typ_groesse]);  // Für Arrays

Beispiel für das Überladen von new und delete

#include <stdlib.h>

class X {

   .

   .

   .

public:

   void* operator new(size_t size) { return newalloc(size);}

   void operator delete(void* p) { newfree(p); }

   X() { /* Initialisieren: hier*/ }

   X(char ch) { /* und hier */ }

   ~X() { /* Aufräumen */ }

   .

   .

   .

};

Hinweis: Destruktoren werden nur aufgerufen, wenn Sie die Compileroption -xd verwenden und eine Exception ausgegeben wurde.

Das Argument groesse liefert die Größe des zu erzeugenden Objekts. Bei newalloc und newfree handelt es sich um vom Anwender bereitgestellte Funktionen zur Speicherreservierung und -freigabe. Konstruktor- und Destruktoraufrufe für Objekte der Klasse X (oder Objekte einer von X abgeleiteten Klasse, die keine eigenen überladenen new- und delete-Operatoren hat) führen zum Aufruf der entsprechenden anwenderdefinierten Operatoren X::operator new() bzw. X::operator delete(). (Destruktoren werden nur dann aufgerufen, wenn Sie die Compiler-Option -xd benutzen und eine Exception auftritt.)

Die Operatorfunktionen X::operator new(), X::operator new[](), X::operator delete() und X::operator delete[]() sind statische Elementfunktionen von X, ganz gleich, ob sie explizit als static deklariert sind oder nicht; es kann sich also nicht um virtuelle Funktionen handeln.

Die vordefinierten globalen Standardoperatoren new(), new[](), delete() und delete[]() können weiterhin im Gültigkeitsbereich von X aufgerufen werden, und zwar entweder explizit mit dem globalen Zugriffsoperator (::operator new(), ::operator new[](), ::operator delete() und ::operator delete[]()) oder implizit beim Erzeugen und Löschen von Objekten, die nicht zur Klasse X oder einer von X abgeleiteten Klasse gehören. Sie können die Standardoperatoren new und delete zum Beispiel wie folgt beim Definieren von überladenen Versionen verwenden:

void* X::operator new(size_t groesse)

{

   void* ptr = new char[s]; // Aufruf des Standardoperators new

     .

     .

     .

   return ptr;

}

void X::operator delete(void* ptr)

{

     .

     .

     .

   delete (void*) ptr;     // Aufruf des Standardoperators delete

}

Das Argument groesse wird deshalb verwendet, weil von X abgeleitete Klassen von X die Operatoren X::operator new() und X::operator new[]() erben. Die Größe eines Objekts einer abgeleiteten Klasse kann sich aber ohne weiteres von der eines Objekts der Basisklasse unterscheiden.


C++ Klassen
Klassen

Siehe auch

C++ Klassen bieten eine Erweiterung des vordefinierten Typsystems. Jeder Klassentyp repräsentiert eine eindeutige Menge von Objekten und stellt Operationen (Methoden) und Konvertierungen zur Verfügung, um diese Objekte zu erzeugen, zu bearbeiten und zu löschen. Es können abgeleitete Klassen definiert werden, die die Elemente einer oder mehrerer Basisklassen (oder übergeordneter Klassen) erben.

In C++ werden Strukturen und Varianten als Klassen mit speziellen standardmäßigen Zugriffsmöglichkeiten betrachtet.

Eine vereinfachte Syntax für Klassendeklarationen lautet:

Klassenschlüssel <Typinfo> Klassenname

<:Basisliste> { <Elementliste> };

Klassenschlüssel kann eines der reservierten Wörter class, struct oder union sein.

Das optionale Typinfo gibt an, daß Laufzeit-Typinformationen über die Klasse angefordert werden. Sie können mit der Compiler-Option -RT compilieren oder das Schlüsselwort _ _rtti verwenden. Nähere Informationen finden Sie in den Erläuterungen zu der Klasse typeinfo.

Die optionale Basisliste zählt die Basisklasse(n) auf, von denen class-name Objekte und Methoden ableiten (oder erben) soll. Wenn zumindest eine Basisklasse angegeben ist, wird die neue Klasse Klassenname abgeleitete Klasse genannt. Die verwendete Basisliste hat voreingestellte und optionale access specifiers, um die Zugriffsrechte der abgeleiteten Klasse auf Elemente der Basisklassen zu verändern.

Die optionale Elementliste deklariert die Klassenelemente (Daten und Funktionen) von Klassenname mit standardmäßigen und optionalen überschreibenden Zugriff-Spezifizierern und legt so fest, welche Elemente auf welche Funktionen zugreifen dürfen.


VCL-Klassendeklarationen

Siehe auch	Beispiel	Schlüsselwort

Syntax

__declspec(<decl-modifier>)

Beschreibung

Das Argument decl-modifier kann entweder delphireturn, delphiclass, oder pascalimplementation sein. Diese Argumente sollten nur in Klassen verwendet werden, die von VCL-Klassen abgeleitet wurden.

Sie müssen __declspec(delphiclass) für alle forward-Deklarationen von Klassen verwenden, die direkt oder indirekt von TObject abgeleitet wurden.

Sie müssen __declspec(delphireturn) verwenden, wenn Sie forward-Deklarationen von Klassen erzeugen, die direkt oder indirekt von Currency, AnsiString, Variant, TDateTime, oder Set abgeleitet werden.

Verwenden Sie den Modifizierer __declspec(pascalimplementation) um anzuzeigen, daß eine Klasse in Object Pascal implementiert wurde. Dieser Modifizierer erscheint in der Pascal-Portabilitäts Header-Datei mit einer Dateierweiterung .hpp.

Das Argument delphireturn wird verwendet, um C++ Klassen für die VCL-kompatible Behandlung in Funktionsaufrufen als Parameter und Rückgabewerte zu markieren.

Das Argument delphiclass wird für die Erzeugung von Klassen verwendet, die die folgende VCL-Kompatibilität haben.

VCL-kompatible RTTI

VCL-kompatibles Konstruktor-/Destruktor-Verhalten

VCL-kompatible Exception-Behandlung

Eine VCL-kompatible Klasse hat folgende Einschränkungen.

Es sind keine virtuellen Basisklassen oder mehrfach-Vererbung erlaubt.

Es muss eine dynamische Speicherplatzzuweisung mit dem globalen Operator new stattfinden.

Kopier- und Zuweisungs-Konstruktoren müssen explizit definiert werden. Der Compiler stellt diese Konstruktoren für Klassen, die von der VCL abgeleitet wurden, nicht automatisch zur Verfügung.


VCL-Klassennamen

Siehe auch	Schlüsselwort

Die integrierte Entwicklungsumgebung von C++Builder erwartet, daß jede VCL-Klasse mit dem Buchstaben „T“ anfängt. Aus diesem Grund wird das erste Vorkommen des Buchstabens „T“ bei der Zuweisung der Programm-Id immer ignoriert. Beispielsweise für eine Deklaration class Test zu einer Programm-Id ohne T.


Klassennamen

Siehe auch

Als Klassenname kann jeder Bezeichner verwendet werden, der innerhalb seines Gültigkeitsbereichs eindeutig ist. Bei Strukturen, Klassen und Varianten kann Klassenname weggelassen werden. Strukturen ohne "Tag"-Name werden unter Strukturen und Typdefinitionen ohne "Tag"  behandelt.


Klassentypen

Siehe auch

Die Deklaration erzeugt einen eindeutigen Klassentyp Klassenname. Damit können Sie weitere Objekte dieses Typs (sog. Instanzen) und von diesem Typ abgeleitete Objekte (z.B. Zeiger auf Klassenname, Referenzen auf Klassenname oder Arrays von Klassenname) definieren:

class X { ... };

X x, &xr, *xptr, xarray[10];

/* vier Objekte: Typ X, Referenz auf X, Zeiger auf X und Array von X */

struct Y { ... };

Y y, &yr, *yptr, yarray[10];

// in C wäre das

// struct Y y, *yptr, yarray[10];

union Z { ... };

Z z, &zr, *zptr, zarray[10];

// in C wäre das

// union Z z, *zptr, zarray[10];

Beachten Sie den Unterschied zwischen C und C++ Struktur- und Variantendeklarationen: Die Schlüsselwörter struct und union sind in C notwendig, während sie in C++ nur gebraucht werden, wenn die Klassennamen - im obigen Beispiel Y und Z - nicht sichtbar sind (siehe Gültiggkeitsbereich von Klassennamen).


Gültigkeitsbereich von Klassennamen

Siehe auch

Klassennamen sind in der Regel lokal gültig. Wenn der Klassenname im selben Gültigkeitsbereich mehr als einmal erscheint, sind einige Besonderheiten zu beachten. Der Gültigkeitsbereich von Klassennamen beginnt bei der Deklaration und endet mit dem einschließenden Block. Wenn ein anderes Objekt, eine andere Klasse oder Funktion oder ein anderer Aufzählungstyp denselben Namen hat, wird der Klassenname im einschließenden Gültigkeitsbereich verdeckt. Wird ein Klassenname in einem Gültigkeitsbereich definiert, der die Definition eines Objekts, einer Funktion oder eines Aufzählungstyps mit demselben Namen enthält, kann auf diese Klasse nur mit Hilfe seines ermittelten Typ-Spezifizierers zugegriffen werden. Das bedeutet, der Klassenschlüssel class, struct oder union muß zusammen mit dem Klassennamen verwendet werden. Zum Beispiel:

struct S { ... };

int S(struct S *Sptr);

void func(void) {

   S t;         // ILLEGALE Deklaration: kein Klassenschlüssel und Funktion S im Gültigkeitsbereich

   struct S s;  // OK: ausgearbeitet mit Klassenschlüssel

   S(&s);       // OK: ist ein Funktionsaufruf

}

C++ erlaubt auch forward-Klassendeklarationen:

class X;  // noch keine Elemente!

Forward-Deklarationen von Klassen ermöglichen die Nutzung spezieller Referenzen auf den Klassennamen X (normalerweise Referenzen auf Zeiger auf die Klassenobjekte), bevor die Klasse vollständig deklariert wird. Weitere Informationen finden Sie unter Deklarationen von Strukturelementen. Bevor Sie Klassenobjekte definieren und verwenden können, müssen Sie jedoch eine vollständige Klassendeklaration mit Elementen vornehmen.

Forward declarations cannot be made for typedef classes.

Siehe auch die Syntax für forward-Deklarationen der VCL-Klassen.


Klassenobjekte

Siehe auch

Klassenobjekte können auf der linken Seite von Zuweisungen verwendet werden (falls das Kopieren erlaubt ist), als Argumente an Funktionen übergeben werden und - mit Einschränkungen - von Funktionen zurückgegeben werden, etc. Viele andere Operationen mit Klassenobjekten und -elementen können vom Benutzer definiert werden. So z.B. durch Element- und Friend-Funktionen oder das Überschreiben von Standardfunktionen und -operatoren, wenn sie mit Objekten einer bestimmten Klasse verwendet werden.

Überschriebene Funktionen und Operatoren werden überladen genannt. Operatoren und Funktionen, deren Gültigkeit auf eine spezielle Klasse (oder auf verknüpfte Klassengruppen) beschränkt ist, werden Elementfunktionen dieser Klasse genannt. C++ verfügt über einen Mechanismus, mit dem derselbe Funktions- oder Operatorname aufgerufen werden kann, um verschiedene - vom Typ oder von der Anzahl der Argumente oder Operanden abhängige - Aufgaben auszuführen.


Klassenelementliste

Siehe auch

Die optionale Elementliste ist eine Folge von Datendeklarationen (jeden Typs, einschließlich Aufzählungstypen, Bitfeldern und anderen Klassen) und Funktionsdeklarationen und -definitionen, für die Speicherklassen-Spezifizierer und Zugriff-Modifizierer verwendet werden können. Die so definierten Objekte werden Klassenelemente genannt. Die Speicherklassen-Spezifizierer auto, extern, und register sind nicht erlaubt, static ist jedoch zulässig.


Elementfunktionen

Siehe auch	Beispiel

Eine Funktion, die ohne den friend-Spezifizierer deklariert wird, erhält die Bezeichnung Elementfunktion der Klasse. Funktionen mit dem friend-Spezifizierer heißen Friend-Funktionen.

Elementfunktionen werden in der Dokumentation von Object Pascal und Delphi als Methoden bezeichnet.

Mehreren Funktionen kann derselbe Name gegeben werden. Voraussetzung dafür ist aber, daß sie sich im Typ oder in der Anzahl der Argumente unterscheiden.


Elementfunktionen-Zugriff

/* Zugriif auf Elementfunktionen einer Basisklasse */

#include <iostream.h>

class X {

public:

   void func1() {cout << "func1" << endl; }

};

class Y : public X {

public:

   void func2()

   {

    cout << "func2" << endl;

    }

};

void main() {

 X myX;

 Y *myY = new Y;

   myX.func1();

   myY ->func2();  // Elementfunktion

   myY ->func1();  // Elementfunktion in Basisklasse

}


Schlüsselwort this

Siehe auch

Nicht-statische Elementfunktionen operieren auf dem Klassenobjekt, mit dem sie aufgerufen werden. Ist z.B. x ein Objekt der Klasse X und f() eine Elementfunktion von X, operiert der Funktionsaufruf x.f() auf x. Ist xptr ein Zeiger auf ein Objekt von X, operiert der Funktionsaufruf xptr->f() auf  *xptr. Wie weiß aber nun die Funktion f, auf welcher Instanz von X sie operiert? C++ übergibt f einen Zeiger auf x, der den Namen this trägt. this wird in allen Aufrufen von nicht-statischen Elementfunktionen als verborgenes Argument übergeben.

this ist eine lokale Variable, die in jeder nicht-statischen Elementfunktion verwendbar ist. this muß nicht deklariert werden und wird in einer Funktionsdefinition nur selten explizit verwendet. Innerhalb der Funktion wird this jedoch implizit verwendet, um Elemente zu referenzieren. Wird z.B. x.f(y) aufgerufen, wobei y ein Element von X ist, erhält this den Wert &x, und y wird zu this->y, was gleichbedeutend mit x.y ist.


Statische Elemente

Siehe auch

Der Speicherklassen-Spezifizierer static kann in Deklarationen von Daten- und Funktionselementen verwendet werden. Derartige Elemente heißen statische Elemente. Sie unterscheiden sich eindeutig von nicht-statischen Elementen. Bei nicht-statischen Elementen "existiert" für jede Klasseninstanz eine eigene Kopie, bei den statischen Elementen ist nur eine Kopie vorhanden. Auf diese kann ohne Referenz auf ein bestimmtes Objekt dieser Klasse zugegriffen werden. Ist x ein statisches Element der Klasse X, dann kann über X::x darauf verwiesen werden (auch dann, wenn noch keine Objekte der Klasse X erzeugt wurden). Es ist jedoch auch möglich, unter Benutzung der normalen Operatoren auf x zuzugreifen, so z.B. mit y.x und yptr->x, wobei y ein Objekt der Klasse X und yptr ein Zeiger auf ein Objekt der Klasse X ist, obwohl die Ausdrücke y und yptr nicht ausgewertet werden. Eine statische Elementfunktion kann mit oder ohne spezielle Elementfunktion-Syntax aufgerufen werden:

class X {

   int member_int;

public:

   static void func(int i, X* ptr);

};

void g(void); {

   X obj;

    func(1, &obj);      // Fehler, wenn es keine globale func()

                       // gibt, die anderweitig definiert ist.

   X::func(1, &obj);   // ruft die statische func() in X auf

                       // nur für statische Funktionen

   obj.func(1, &obj);   // ebenso (für statische und

                       // nicht-statische Funktionen)

}

Da eine statische Elementfunktion ohne ein spezielles Objekt aufgerufen werden kann, hat sie keinen this-Zeiger. Sie kann daher nicht ohne explizite Angabe eines Objekts mit . oder -> auf nicht-statische Elemente zugreifen. Wenn wir das vorherige Beispiel zugrunde legen, könnte func z.B. wie folgt definiert werden:

void X::func(int i, X* ptr)

{

    member_int = i;       // Auf welches Objekt bezieht sich

                         // member_int? Fehler!

    ptr->member_int = i;  // OK: Jetzt wissen wir Bescheid.

}

Abgesehen von Inline-Funktionen haben statische Elementfunktionen globaler Klassen externe Bindung. Statische Elementfunktionen können keine virtuellen Funktionen sein. Es ist nicht erlaubt, eine statische und eine nicht-statische Elementfunktion mit demselben Namen und denselben Argumenttypen zu versehen.

Die Deklaration eines statischen Datenelements innerhalb seiner Klasse ist noch keine Definition. Die Definition muß deshalb an anderer Stelle erfolgen, um Speicherplatz zu reservieren und eine Initialisierung durchzuführen.

Statische Elemente einer Klasse, die lokal zu einer Funktion deklariert sind, haben keine Bindung und können nicht initialisiert werden. Statische Elemente einer globalen Klasse können wie normale globale Objekte initialisiert werden, allerdings nur im Gültigkeitsbereich einer Datei. Abgesehen davon, daß sie initialisiert werden können, gehorchen beliebig tief geschachtelte statische Elemente den normalen Zugriffsregeln für Klassen.

class X {

   static int x;

   class inner {

      static float f;

      void func(void);     // geschachtelte Deklaration

      };

};

int X::x = 1;

float X::inner::f = 3.14;  // Initialisierung der geschachtelten statischen Funktion

X::inner::func(void) {    /* Definition der geschachtelten Funktion */  }

Der Hauptzweck statischer Elemente ist es, Daten aufzunehmen, die allen Objekten einer Klasse gemeinsam sind (z.B. die Anzahl von erzeugten Objekten oder die zuletzt verwendete Ressource aus einem Pool, aus dem sich alle diese Objekte bedienen). Statische Elemente werden außerdem genutzt, um

 	Die Anzahl der sichtbaren globalen Namen zu reduzieren:

 	Offensichtlich zu machen, welches statische Objekt logisch zu welcher Klasse gehört:

 	Den Zugriff auf ihre Namen zu erlauben.


Inline-Funktionen

Siehe auch

Sie können eine Elementfunktion innerhalb einer Klasse deklarieren und an anderer Stelle definieren. Alternativ dazu ist es möglich, die Elementfunktion innerhalb einer Klasse zu deklarieren und zu definieren. In diesem Fall wird die Funktion Inline-Funktion genannt.

C++Builder kann manchmal den normalen Funktionsaufruf-Overhead reduzieren, indem es anstelle des Funktionsaufrufs direkt den compilierten Code der Funktion einsetzt. Dieser Vorgang, der Inline-Erweiterung genannt wird, hat keinen Einfluß auf den Gültigkeitsbereich des Funktionsnamens oder seiner Argumente. Eine Inline-Ausdehnung ist aber nicht immer möglich oder nützlich. Der inline-Spezifizierer ist ein Hinweis für den Compiler, daß Sie eine Inline-Ausdehnung wünschen.

Hinweis:	Die Inline-Erweiterung kann durch den C++Builder Compiler unterbunden werden.

Explizite und implizite inline-Anweisungen werden am besten nur für kleine, häufig benutzte Funktionen verwendet, wie z.B. Operatorfunktionen, die überladene Operatoren implementieren. So ist beispielsweise die Klassendeklaration von func:

int i;                          // global int

class X {

public:

   char* func(void) { return i; }  // standardmäßig inline

   char* i;

};

gleichbedeutend mit:

inline char* X::func(void) { return i; }

func ist außerhalb der Klasse mit dem expliziten inline-Spezifizierer definiert. Das von func zurückgegebene i ist der char* i der Klasse X (siehe Gültigkeitsbereich von Elementen).

Inline-Funktionen und Ausnahmen

Die inline-Anweisung für Funktionen, die eine Ausnahme-Angabe enthalten, wird von C++Builder ignoriert. Beispiel:

inline void f1() throw(int)

   {

   // Warnung: Funktionen mit Ausnahme werden nicht inline behandelt

   }

Die übrigen Beschränkungen gelten nur, wenn das Aufräumen mit Destruktoren aktiviert ist.

Hinweis:	Destruktoren werden standardmäßig aufgerufen. Informationen über Optionen zur Ausnahme-Behandlung finden Sie unter Einstellung von Ausnahmebehandlungsoptionen.

Eine Inline-Funktion, die mindestens einen Parameter übernimmt, der den Typ "Klasse mit Destruktor" hat, wird nicht inline behandelt. Beachten Sie, daß diese Beschränkungen nicht für Klassen gilt, die als Referenz übergeben werden. Beispiel:

struct foo {

   foo();

   ~foo();

   };

inline void f2(foo& x) {

   // keine Warnung; f2() läßt sich inline behandeln

   }

inline void f3(foo x) {

   // Warnung: Funktionen, die Klassenargumente als Wertparameter

   // übernehmen, werden in Funktion f3(foo) nicht inline

   }

Eine Inline-Funktion, die eine Klasse mit Destruktor als Wert zurückgibt, wird nicht inline behandelt, wenn innerhalb des Rückgabeausdrucks Variablen oder temporäre Objekte zu entfernen sind:

struct foo {

   foo();

   ~foo();

   };

inline foo f4() {

   return foo();

   // keine Warnung; f4() läßt sich inline behandeln

   }

inline foo f5() {

   foo X;

   return foo(); // Objekt X muß entfernt werden

   // Warnung: Funktionen mit bestimmten Rückgabeanweisungen

   //          werden in Funktion f5() nicht behandelt

   }

inline foo f6() {

   return ( foo(), foo() );  // temporäres Objekt im Rückgabewert

   // Warnung: Funktionen mit einigen Rückgabeanweisungen

   //          werden in Funktion f6() nicht inline behandelt

   }


Gültigkeitsbereich von Elementen

Siehe auch

Der Ausdruck X::func()  im Beispiel zu den Inline-Funktionen und Ausnahmen wird der Klassenname X mit dem Gültigkeitsbereichs-Zugriffsmodifizierer verwendet. Dadurch wird angezeigt, daß func als Elementfunktion von X betrachtet wird und den gleichen Gültigkeitsbereich wie X  besitzt, obwohl es "außerhalb" der Klasse definiert wurde. Die Influenz von X:: wird im Rumpf der Definition erweitert. Dadurch wird klar, warum das von func zurückgegebene i auf X::i, char* i von X und nicht auf das globale int i verweist. Ohne den Modifizierer X:: wäre die Funktion func eine normale Funktion, die keiner Klasse angehört, und würde folglich den globalen int i zurückgeben.

Alle Elementfunktionen sind also im Gültigkeitsbereich ihrer Klasse, auch wenn sie außerhalb der Klasse definiert wurden.

Auf Datenelemente der Klasse X kann mit Hilfe der Auswahloperatoren . und -> zugegriffen werden (wie in C-Strukturen). Dies gilt auch für Elementfunktionen (siehe Das Schlüsselwort this). Zum Beispiel:

class X {

public:

   int i;

   char name[20];

   X *ptr1;

   X *ptr2;

   void Xfunc(char*data, X* left, X* right);   // wird an anderer Stelle definiert

};

void f(void);

{

   X x1, x2, *xptr=&x1;

   x1.i = 0;

   x2.i = x1.i;

   xptr–>i = 1;

   x1.Xfunc("stan", &x2, xptr);

}

Für ein Element oder Basiselement m der Klasse X nennt man den Ausdruck X::m einen qualifizierten Namen. Er hat den gleichen Typ wie m und ist nur dann ein L-Wert, wenn auch m ein L-Wert ist. Auf das korrekte Element m kann damit auch dann zugegriffen werden, wenn der Klassenname X durch einen anderen Namen verdeckt ist (durch Verwendung von X::m).

Es ist nicht möglich, in einem anderen Programmteil als der Klassendeklaration selbst Elemente einer Klasse hinzuzufügen. Die Klasse X kann nicht Objekte derselben Klasse X enthalten, wohl aber Zeiger oder Referenzen auf Objekte derselben Klasse X (ähnlich wie bei C-Strukturen und Varianten).


Geschachtelte Typen

Siehe auch

"Tag"- oder  typedef-Namen, die innerhalb einer Klasse deklariert werden, gehören lexikalisch zum Gültigkeitsbereich dieser Klasse. Auf derartige Namen kann generell nur über die Notation xxx::yyy zugegriffen werden - wenn der aktuelle Gültigkeitsbereich nicht mit dem der einschließenden Klasse übereinstimmt.

Eine Klasse, die innerhalb einer anderen Klasse deklariert wird, heißt geschachtelte Klasse. Ihr Name ist lokal bezüglich der sie einschließenden Klasse; die geschachtelte Klasse liegt im Gültigkeitsbereich der sie einschließenden Klasse. Dies kann man als rein lexikalische Schachtelung bezeichnen. Die geschachtelte Klasse besitzt keinerlei zusätzliche Privilegien für den Zugriff auf Elemente der sie einschließenden Klasse (dies gilt umgekehrt auch für die einschließende Klasse).

Klassen können auf diese Art und Weise in beliebige Tiefe geschachtelt werden. Geschachtelte Klassen lassen sich innerhalb einer Klasse deklarieren und später definieren. Ein Beispiel:

struct outer

{

   typedef int t;  // 'outer::t' ist ein typedef-Name

   struct inner    // 'outer::inner' ist eine°Klasse

   {

      static int x;

   };

   static int x;

      int f();

   class deep;     // geschachtelte Deklaration

};

int outer::x;     // definiert statisches Datenelement

int outer::f() (

   t x;           // 't' hier direkt sichtbar

   return x;

   }

int outer::inner::x;    // definiert statisches Datenelement

outer::t x;             // Hier muß 'outer::t' verwendet werden.

class outer::deep { };  // geschachtelte Klasse hier definieren

In der Version 2.0 von C++Builder  gehören "Tag"-Namen und typedef-Namen, die innerhalb einer Klasse deklariert werden, zu dem globalen (Datei-)Gültigkeitsbereich. Beispiel:

struct foo

{

   enum bar { x };   // 2.0 Regeln: 'bar' gehört zum Dateiültigkeitsbereich

                     // 2.1 Regeln: 'bar' gehört zum 'foo'-Gültigkeitsbereich

};

bar x;

Der obige Programmtext wird fehlerfrei compiliert. Da er jedoch entsprechend den Regeln für Version 2.1 nicht korrekt ist, wird folgende Warnung ausgegeben:

Warning: Use qualified name to access nested type 'foo::bar'


Element-Zugriffskontrolle

Siehe auch

Elemente einer Klasse erhalten Zugriffsattribute entweder standardmäßig (abhängig von Klassenschlüsseln und dem Ort der Deklaration) oder durch einen der drei folgenden Zugriff-Spezifizierer: public, private und protected. Es folgt die Bedeutung dieser Attribute:

public: Das Element kann von jeder Funktion benutzt werden.

private: Das Element kann nur von Elementfunktionen und Friends der Klasse, in der es deklariert wurde, benutzt werden.

protected: Es gilt das gleiche wie für private. Zusätzlich ist jedoch die Benutzung durch Elementfunktionen und Friends von Klassen, die von der deklarierten Klasse abgeleitet sind, möglich. Letzteres gilt allerdings nur für Objekte vom abgeleiteten Typ. (Abgeleitete Klassen werden in Zugriff auf Basis- und abgeleitete Klassen besprochen.)

Hinweis:	Friend Funktionsdeklarationenen sind nicht von Zugriff-Spezifizierern beeinflußt (Mehr Informationen erhalten Sie unter dem Stichwort Friends von Klassen).

Elemente einer Klasse sind standardmäßig private. Sie müssen daher explizit den Zugriff-Spezifizierer public oder protected verwenden, um die Voreinstellung zu überschreiben.

Elemente einer Struktur (struct) sind standardmäßig public; Sie können diese Voreinstellung jedoch mit dem Zugriff-Spezifizierer private oder protected überschreiben.

Elemente einer Variante (union) sind standardmäßig public; diese Einstellung kann nicht geändert werden. Die Angabe eines Zugriff-Spezifizierers ist in Verbindung mit Varianten-Elementen nicht zulässig.

Ein Zugriff-Spezifizierer bleibt für alle folgenden Elementdeklarationen gültig, bis ein anderer Spezifizierer auftritt. Ein Beispiel:

class X {

   int i;    // X::i ist standardmäßig private

   char°ch;  // das gilt auch für X::ch

public:

   int j;    // die nächsten zwei sind public

   int k;

protected:

   int l;    // X::l ist protected

};

struct Y {

   int i;    // Y::i ist standardmäßig public

private:

   int j;    // Y::j ist private

public:

   int k;    // Y::k ist public

};

union Z {

   int i;    // ist standardmäßig public; keine andere Möglichkeit

   double d;

};

Hinweis:	Die Zugriff-Spezifizierer können in jeder beliebigen Reihenfolge aufgeführt und gruppiert werden. Sie können sich die Mühe der Typisierung ersparen, indem Sie alle private Elemente gemeinsam deklarieren, u.s.w..


Zugriff auf Basisklassen und abgeleitete Klassen

Siehe auch

Bei der Deklaration einer abgeleiteten Klasse D werden die Basisklassen B1, B2, ... durch Komma getrennt in einer Basisliste aufgeführt:

Klassenschlüssel D : Basisliste { <Elementliste> }

D erbt alle Elemente dieser Basisklassen. (Neu definierte Basisklassen-Elemente werden auch geerbt; der Zugriff auf sie kann falls erforderlich durch Änderung des Gültigkeitsbereichs erfolgen.) D kann nur die public- und protected-Elemente seiner Basisklassen benutzen. Welche Zugriffsattribute erhalten nun aber die geerbten Elemente, von D aus gesehen? Angenommen, D will ein public-Element aus einer Basisklasse benutzen, es für außenstehende Funktionen aber als private-Element schützen. Die Lösung liegt im Gebrauch der Zugriff-Spezifizierer in der Basisliste.

Hinweis:	Da eine Basisklasse selbst eine abgeleitete Klasse sein kann, lassen sich die Zugriffsattribute rekursiv anwenden: Man geht zurück, bis die erste(n) Basisklasse(n) erreicht ist(sind) - die, die nicht erbt(en).

Bei der Deklaration von D können Sie die Zugriff-Spezifizierer public, protected oder private in die Basisliste vor den Klassen einsetzen:

class D : public B1, private B2, ... {

  .

  .

  .

}

Diese Modifizierer ändern nicht die Zugriffsattribute der Basiselemente (von der Basisklasse aus gesehen), können aber die Zugriffsattribute der Basiselemente (von der abgeleiteten Klasse aus gesehen) ändern.

Ist D eine class-Deklaration, wird standardmäßig der Zugriff-Spezifizierer private verwendet. Dagegen ist public der Standard, wenn D eine struct-Deklaration ist.

Hinweis:	Varianten können keine Basisklassen enthalten und dürfen nicht als Basisklassen verwendet werden.

Eine abgeleitete Klasse erbt ihre Zugriffsattribute von einer Basisklasse wie folgt:

 	public-Basisklasse: public-Elemente der Basisklasse werden public-Elemente der abgeleiteten Klasse. protected-Elemente der Basisklasse werden protected-Elemente der abgeleiteten Klasse. private-Elemente der Basisklasse bleiben private-Elemente der Basisklasse.

 	protected-Basisklasse: Sowohl public- als auch protected-Elemente der Basisklasse werden protected-Elemente der abgeleiteten Klasse. private-Elemente der Basisklasse bleiben private-Elemente der Basisklasse.

 	private-Basisklasse: Sowohl public- als auch protected-Elemente der Basisklasse werden private-Elemente der abgeleiteten Klasse. private-Elemente der Basisklasse bleiben private-Elemente der Basisklasse.

Beachten Sie, daß private-Elemente einer Basisklasse von den Elementfunktionen der abgeleiteten Klasse nicht benutzt werden können. Dies gilt allerdings nicht, wenn friend-Deklarationen explizit einen Zugriff erlauben. Zum Beispiel:

/* class X  ist abgeleitet von class A */

class X : A {                   // private A ist der Default für class

  .

  .

  .

}

/* class Y ist durch Mehrfachvererbung von B und C abgeleitet

   B ist standardmäßig private B */

class Y : B, public C {     // überschreibt den Default für C

  .

  .

  .

}

/* struct S ist von D abgeleitet */

struct S : D {              // public D ist der Default für struct

  .

  .

  .

}

/* struct T ist durch Mehrfachvererbung von D und E abgeleitet

   E ist per Default public E */

struct T : private D, E {   // überschreibt den Default für D

                            // E ist per Default public

  .

  .

  .

}

Die Wirkung von Zugriff-Spezifizierern in der Basisliste kann durch Verwendung von qualifizierten Namen in der public- oder protected-Deklaration der abgeleiteten Klasse noch weiter geändert werden. Zum Beispiel:

class B {

   int a;               // per Default private

public:

   int b,c;

   int Bfunc(void);

};

class X : private B {   // a, b, c und Bfunc sind nun private in X

   int d;               // per Default private; auf a kann

                        //in X nicht zugegriffen werden

public:

   B::c;                // c war private; jetzt public

   int e;

   int Xfunc(void);

};

int Efunc(X& x);        // extern zu B und X

Die Funktion Efunc() kann nur die public-Namen c, e und Xfunc() verwenden.

Die Funktion Xfunc ist ein Element der Klasse X, die von private B abgeleitet ist. Xfunc hat demnach Zugriff auf

 	c, dessen Zugriffsattribut in public geändert wurde,

 	die Elemente b und Bfunc() von B, deren Zugriffsattribute in private-für-X geändert wurden,

 	die private- und public-Elemente d, e und Xfunc(), die der Klasse X eigen sind.

Xfunc() kann jedoch nicht auf das Element a zugreifen, das "für B private" ist.


Virtuelle Basisklassen

Siehe auch

Eine virtuelle Klasse ist eine Basisklasse, die an mehr als eine Klasse abgeleitet wird, so wie es bei einer Mehrfachvererbung vorkommen kann.

Eine Basisklasse kann nicht mehr als einmal in einer abgeleiteten Klasse spezifiziert werden:

class B { ...};

class D : B, B { ... };  // UNGÜLTIG

Allerdings ist es möglich, eine Basisklasse indirekt mehrmals an eine abgeleitete Klasse zu übergeben:

class X : public B { ... }

class Y : public B { ... }

class Z : public X, public Y { ... }  // OK

In diesem Fall hat jedes Objekt der Klasse Z zwei Unterobjekte der Klasse B.

Falls dies zu Problemen führt, kann dem Basisklassen-Spezifizierer das Schlüsselwort virtual hinzugefügt werden. Zum Beispiel:

class X : virtual public B { ... }

class Y : virtual public B { ... }

class Z : public X, public Y { ... }

B ist nun eine virtuelle Basisklasse, und die Klasse Z hat nur ein Unterobjekt der Klasse B.

Konstruktoren für virtuelle Basisklassen

Konstruktoren für virtuelle Basisklassen werden vor allen Konstruktoren nicht-virtueller Basisklassen aufgerufen.

Wenn die Hierarchie viele virtuelle Basisklassen enthält, werden die virtuellen Basisklassenkonstruktoren in der Reihenfolge aufgerufen, in der sie deklariert wurden.

Alle nicht-virtuellen Basisklassen sind dann konstruiert, bevor der abgeleitete Klassenkonstruktor aufgerufen wird.

Wenn eine virtuelle Klasse von einer nicht-virtuellen Basis abgeleitet ist, dann wird die nicht-virtuelle Basis zuerst vorhanden sein, so daß die virtuelle Basisklasse richtig konstruiert werden kann. Der folgende Quelltext

class X : public Y, virtual public Z

   X one;

erzeugt beispielsweise diese Reihenfolge:

Z();   // Initialisierung der virtuellen Basisklasse

Y();   // nicht-virtuelle Basisklasse

X();   // abgeleitete Klasse


Friends von Klassen

Siehe auch

Ein friend F der Klasse X ist eine Funktion oder Klasse, die - obwohl kein Element der Klasse X - volle Zugriffsrechte auf die private- und protected-Elemente von X hat. In bezug auf Gültigkeitsbereich, Deklaration und Definition wird F wie eine normale Funktion behandelt.

Da F kein Element von X ist, liegt es nicht im Gültigkeitsbereich von X und kann deshalb nicht mit den Auswahloperatoren x.F bzw. xptr->F aufgerufen werden (wobei x ein Objekt von X und xptr ein Zeiger auf ein Objekt von X ist).

Um eine Funktion zum Friend einer Klasse zu machen, deklariert oder definiert man sie in dieser Klasse mit dem Spezifizierer friend.

friend-Funktionen, die innerhalb einer Klasse definiert sind, gehorchen denselben Inline-Regeln wie Elementfunktionen (siehe Inline-Funktionen). friend-Funktionen werden weder durch ihre Stellung innerhalb einer Klasse noch durch irgendwelche Zugriff-Spezifizierer beeinflußt. Dazu ein Beispiel:

class X {

   int i;                           // private zu X

   friend void friend_func(X*, int);

/* friend_func ist nicht private, obwohl sie im private-Abschnitt deklariert wird */

public:

     void member_func(int);

};

/* Definitionen; beide Funktionen greifen auf private int i zu */

void friend_func(X* xptr, int a) { xptr–>i = a; }

void X::member_func(int a) { i = a; }

X xobj;

/* Man beachte den Unterschied in den Funktionsaufrufen */

friend_func(&xobj, 6);

xobj.member_func(6);

Alle Funktionen der Klasse Y können mit einer einzigen Deklaration zu Friends der Klasse X gemacht werden:

class Y;                      // unvollständige Deklaration

class X {

   friend Y;

   int i;

   void member_funcX();

};

class Y; {                    // Deklaration wird vervollständigt

   void friend_X1(X&);

   void friend_X2(X*);

  .

  .

  .

};

Die in Y deklarierten Funktionen sind Friends von X, obwohl sie keinen friend-Spezifizierer haben. Sie haben Zugriff auf die private-Elemente von X wie beispielsweise i oder member_funcX.

Es ist außerdem möglich, daß eine einzelne Elementfunktion der Klasse X ein Friend der Klasse Y wird:

class X {

  .

  .

  .

   void member_funcX();

}

class Y {

   int i;

   friend void X::member_funcX();

  .

  .

  .

};

Freundschaften zwischen Klassen sind nicht transitiv: Ist z.B. X Friend von Y und Y Friend von Z, heißt das nicht, daß X Friend von Z sein muß. Freundschaften werden nicht vererbt.


Einführung in Konstruktoren und Destruktoren

Siehe auch

Einige spezielle Elementfunktionen bestimmen, wie die Objekte einer Klasse erzeugt, initialisiert, kopiert und gelöscht werden. Konstruktoren und Destruktoren sind die wichtigsten dieser Funktionen. Sie verfügen über viele Eigenschaften der normalen Elementfunktionen. Beispielsweise  werden sie innerhalb einer Klasse deklariert und definiert - oder innerhalb einer Klasse deklariert  und außerhalb definiert. Sie haben allerdings noch einige besondere Eigenschaften:

 	Sie haben keine Rückgabewert-Deklaration (auch nicht void).

 	Sie können nicht vererbt werden, allerdings kann eine abgeleitete Klasse die Konstruktoren und Destruktoren der Basisklasse aufrufen.

 	Konstruktoren können - wie die meisten C++ Funktionen - Parameterlisten mit Vorgabewerten haben oder die Initialisierungslisten der Elemente benutzen.

 	Destruktoren können virtual sein, Konstruktoren nicht. (Siehe Virtuelle Destruktoren.)

Ihre Adressen können nicht bestimmt werden.

int main (void)

{

.

.

.

void *ptr = base::base;    // ungültig

.

.

.

}

 	Konstruktoren und Destruktoren können von C++Builder generiert werden, wenn sie nicht explizit vom Benutzer definiert wurden. Sie werden außerdem bei vielen Gelegenheiten im Programm aufgerufen, ohne daß im Quelltext ein expliziter Aufruf steht. Jeder Konstruktor oder Destruktor, der vom Compiler generiert wird, ist public.

 	Konstruktoren können nicht wie eine normale Funktion aufgerufen werden. Dagegen ist der Aufruf von Destruktoren möglich, wenn ihr vollständig qualifizierter Name verwendet wird.

{

.

.

.

X *p;

.

.

.

p–>X::~X();                // legaler Aufruf des Destruktors

X::X();                    // illegaler Aufruf des Destruktors

.

.

.

}

 	Bei der Definition bzw. dem Löschen von Objekten ruft der Compiler automatisch Konstruktoren und Destruktoren auf.

 	Konstruktoren und Destruktoren können implizit die Operatoren new und delete aufrufen, wenn eine Speicherplatzreservierung für ein Objekt nötig ist.

 	Ein Objekt, das einen Konstruktor oder Destruktor enthält, kann nicht als Element einer Variante verwendet werden.

 	Wenn für eine Klasse X kein Konstruktor zur Aufnahme eines gegebenen Typs definiert wurde, wird nicht versucht, andere Konstruktoren oder Konvertierungsfunktionen zu finden, um den zugewiesenen Wert in einen Typ umzuwandeln, der von einem Konstruktor für Klasse X akzeptiert wird. Beachten Sie, daß diese Regel nur für Konstruktoren mit einem Parameter gilt, und wenn keine Initialisierer mit der Syntax "=" verwendet werden.

class X { /* ... */ X(int); };

class Y { /* ... */ Y(X); };

Y a = 1;                   // illegal: Y(X(1))  nicht versucht

Hat class X einen oder mehrere Konstrukoren, wird bei jeder Definition eines Objekts vom Typ x of class X einer davon aufgerufen. Der Konstrukor erzeugt und initialisiert x. Destruktoren machen diesen Vorgang wieder rückgängig, indem sie das von den Konstruktoren erzeugte Objekt der Klasse löschen.

Konstruktoren werden auch bei der Erzeugung eines lokalen oder temporären Objekts einer Klasse aufgerufen. Destruktoren werden aufgerufen, wenn deren Gültigkeitsbereich verlassen wird.


Konstruktoren

Siehe auch

Konstruktoren unterscheiden sich von allen anderen Elementfunktionen dadurch, daß sie den Namen der Klasse tragen, zu der sie gehören. Wenn ein Objekt dieser Klasse erzeugt oder kopiert wird, wird der entsprechende Konstruktor implizit aufgerufen.

Konstruktoren für globale Variablen werden vor dem Aufruf der Funktion main aufgerufen. Wird die Direktive #pragma startup zur Installation einer Funktion vor der main-Funktion verwendet, werden die Konstruktoren für globale Variablen vor dieser Funktion aufgerufen.

Lokale Objekte werden erzeugt, wenn der Gültigkeitsbereich der Variablen aktiv wird. Der Aufruf eines Konstruktor erfolgt außerdem, wenn ein temporäres Objekt der Klasse erzeugt wird.

class X {

public:

   X();   // Konstruktor der Klasse X

};

Ein Konstruktor von class X kann nicht X als Argument haben:

class X {

public:

   X(X);                      // ungültig

};

Die Übergabeparameter für einen Konstruktor können jeden Typ außer dem der Klasse, deren Element er ist, haben. Der Konstruktor kann eine Referenz auf seine eigene Klasse als Parameter erhalten. In diesem Fall wird er Kopierkonstruktor genannt. Ein Konstruktor, an den keine Argumente übergeben werden können, heißt Standardkonstruktor. Der Standardkonstruktor und der Kopierkonstruktor werden in den nächsten Abschnitten besprochen.


Standardkonstruktor

Siehe auch

An den Standardkonstruktor für class X werden keine Argumente übergeben; er hat normalerweise die Form X::X(). Wenn kein benutzerdefinierter Konstruktor für eine Klasse existiert, erzeugt C++Builder einen Standardkonstruktor. Bei der Deklaration eines Objekts innerhalb einer Klasse, wie z.B. X x, generiert der Standardkonstruktor das Objekt x.

Wie alle Funktionen können Konstruktoren Parameter mit Standardargumenten haben. Beispielsweise nimmt der Konstruktor:

X::X(int, int = 0)

ein oder zwei Argumente entgegen. Falls das zweite Argument beim Aufruf fehlt, wird 0 int eingesetzt. Ähnlich nimmt der Konstruktor

X::X(int = 5, int = 6)

ein, zwei oder auch kein Argument entgegen. Der Standardkonstruktor X::X() akzeptiert kein Argument und darf nicht mit beispielsweise X::X(int = 0) verwechselt werden, der ein Argument oder - als Standardkonstruktor - kein Argument akzeptiert.

Vermeiden Sie daher bei der Verwendung von Konstruktoren zweideutige Konstruktionen. Im folgenden Fall können die beiden Standardkonstruktoren nicht klar auseinander gehalten werden:

class X

{

public:

   X();

   X(int i = 0);

};

int main() {

   X one(10);  // OK; verwendet X::X(int)

   X two;      // ungültig; nicht klar, ob X::X() oder

               // X::X(int = 0)

   return 0;

}


Kopierkonstruktor

Siehe auch

Ein Kopierkonstruktor für class X kann mit einem einzelnen Argument vom Typ X wie folgt aufgerufen werden:

X::X(X&)

oder

X::X(const X&)

oder

X::X(const X&, int = 0)

In einem Kopierkonstruktor sind auch Standardargumente erlaubt. Kopierkonstruktoren werden zum Initialisieren eines Klassenobjekts eingesetzt - typischerweise, wenn Sie ein Objekt durch Initialisierung mit einem anderen Klassenobjekt deklarieren:

X x1;

X x2 = x1;

X x3(x1);

C++Builder generiert einen Kopierkonstruktor für class X, wenn einer benötigt wird und in class X keiner definiert wurde. Der vom C++Builder Compiler generierte Kopierkonstruktor ermöglicht einen sicheren Einstieg in das Programmieren mit einfachen Datentypen. Sie müssen Ihren eigenen Kopierkonstruktor nur definieren, wenn Ihr Programm zusammengesetzte, komplexe Typen, wie beispielsweise class, struct und Arrays, erzeugt. Bei der Verwendung einer Klasse als Funktionsparameter mit Wertübergabe wird ebenfalls der Kopierkonstruktor aufgerufen.

Siehe auch elementweiser Klassenzugriff. Sie sollten den Kopierkonstruktor definieren, wenn Sie den Zugriffsoperator überladen.


Überladen von Konstruktoren

Siehe auch

Konstruktoren können überladen werden. Dadurch ist die Erzeugung von Objekten möglich, die von den Initialisierungswerten abhängig sind.

class X {

   int    integer_part;

   double double_part;

public:

   X(int i)    { integer_part = i; }

   X(double d) { double_part = d; }

};

int main() {

   X one(10);   // ruft X::X(int) auf und setzt integer_part auf 10

   X one(3.14); // ruft X::X(double) auf und setzt double_part auf 3.14

   return 0;

}


Aufrufreihenfolge von Konstruktoren

Siehe auch

Hat eine Klasse eine oder mehrere Basisklassen, werden die Basisklassen-Konstruktoren vor dem Konstruktor der abgeleiteten Klasse aufgerufen. Die Basisklassen-Konstruktoren werden in der Reihenfolge ihrer Deklaration aufgerufen.

Beispielsweise werden hier,

class Y {...}

class X : public Y {...}

X one;

die Konstruktoren in folgender Reihenfolge aufgerufen:

Y();   // Basisklassenkonstruktor

X();   // abgeleiteter Klassenkonstruktor

Gibt es mehrere Basisklassen,

class X : public Y, public Z

X one;

werden die Konstruktoren in der Reihenfolge ihrer Deklaration aufgerufen:

Y();  // Basisklassenkonstruktoren kommen zuerst

Z();

X();

Konstruktoren für virtuelle Basisklassen werden vor denen für nicht-virtuelle Basisklassen aufgerufen. Sind mehrere virtuelle Basisklassen vorhanden, werden die zugehörigen Konstruktoren in der Reihenfolge ihrer Deklaration aufgerufen. Danach werden die Konstruktoren der nicht-virtuellen Basisklassen aufgerufen, darauf folgen die der abgeleiteten Klasse.

Wurde eine virtuelle Klasse von einer nicht-virtuellen Basisklasse abgeleitet, wird diese zuerst behandelt, um sicherzustellen, daß der Konstruktor der virtuellen Klasse richtig arbeitet. Der Programmteil:

class X : public Y, virtual public Z

X one;

führt zu folgender Reihenfolge:

Z();   // Initialisierung der virtuellen Basisklasse

Y();   // nicht-virtuelle Basisklasse

X();   // abgeleitete Klasse

Ein etwas komplexeres Beispiel:

class base;

class base2;

class level1 : public base2, virtual public base;

class level2 : public base2, virtual public base;

class toplevel : public level1, virtual public level2;

toplevel view;

Die Abarbeitungsreihenfolge für Konstruktoren für view ist wie folgt definiert:

base();      // virtuelle Basisklasse ist die höchste in der Hierarchie

             // base wird nur einmal initialisiert

base2();     // die nicht-virtuelle Basis der virtuellen Basis level2

             // muß zur Initialisierung von level2 aufgerufen werden

level2();    // virtuelle Basisklasse

base2();     // nicht-virtuelle Basis von level1

level1();    // weitere nicht-virtuelle Basis

toplevel();

Falls eine Klassenhierarchie mehrere Instanzen derselben virtuellen Basisklasse enthält, wird der Konstruktor dieser Basisklasse nur einmal aufgerufen. Existieren jedoch virtuelle und nicht-virtuelle Instanzen der Basisklasse, wird der Klassenkonstruktor einmal für alle virtuellen Instanzen und dann jeweils einmal für jede nicht-virtuelle Instanz der Basisklasse aufgerufen.

Konstruktoren für Elemente eines Arrays werden in aufsteigender Reihenfolge des Index aufgerufen.


Initialisierung von Klassen

Siehe auch

Ein Objekt einer Klasse, das nur public-Elemente und keine Konstruktoren oder Basisklassen enthält (dies ist normalerweise eine Struktur), kann mit einer Initialisierer-Liste initialisiert werden. Hat eine Klasse einen Konstruktor, müssen ihre Objekte entweder initialisiert werden oder einen Standardkonstruktor haben. Der Standardkonstruktor wird für Objekte genutzt, die nicht explizit initialisiert werden.

Objekte von Klassen mit Konstruktoren können mit einer Liste von Ausdrücken in Klammern initialisiert werden. Diese Liste wird als Argumentliste für den Konstruktor verwendet. Eine Alternative dazu ist das Gleichheitszeichen (=), gefolgt von einem einzelnen Wert. Dieser Wert kann vom Typ des ersten Arguments sein, das ein Konstruktor dieser Klasse enthält. In diesem Fall gibt es keine weiteren Argumente, oder die restlichen Argumente haben Standardwerte. Als Initialisierungswert kann auch ein Objekt des Klassentyps verwendet werden. Im ersten Fall wird der zugehörige Konstruktor aufgerufen, um das Objekt zu erzeugen. Im zweiten Fall wird zur Initialisierung der Kopierkonstruktor aufgerufen.

class X

{

   int i;

public:

   X();         // Funktionsrümpfe wurden der Übersichtlichkeit halber weggelassen.

   X(int x);

   X(const X&);

};

void main()

{

   X one;        // Standardkonstruktor wird aufgerufen

   X two(1);     // Konstruktor X::X(int) wird verwendet

   X three = 1;  // ruft X::X(int) auf

   X four = one; // ruft X::X(const X&) zum Kopieren auf

   X five(two);  // ruft X::X(const X&) auf

}

Der Konstruktor kann seinen Elementen auf zweierlei Arten Werte zuweisen:

 	Er kann die Werte als Parameter übernehmen und sie den Elementvariablen im Rumpf der Konstruktorfunktion zuweisen:

class X

{

   int a, b;

public:

   X(int i, int j) { a = i; b = j }

};

 	Er kann aber auch eine Initialisierer-Liste verwenden, die vor dem Funktionsrumpf steht:

class X

{

   int a, b, &c;  // Beachten Sie die Referenzvariable.

public:

   X(int i, int j) : a(i), b(j), c(a) {}

};

Eine Referenzvariable kann nur in einer Initialisierer-Liste initialisiert werden.

In beiden Fällen weist eine Initialisierung von X x(1, 2) x::a den Wert 1 und x::b den Wert 2 zu. Die zweite Methode - die Initialisierer-Liste - ist ein Mechanismus zur Übergabe von Werten an Basisklassen-Konstruktoren.

Hinweis:	Basisklassen-Konstruktoren müssen als public oder protected deklariert sein, damit sie von einer abgeleiteten Klasse genutzt werden können.

class base1

{

   int x;

public:

   base1(int i) { x = i; }

};

class base2

{

   int x;

public:

   base2(int i) : x(i) {}

};

class top : public base1, public base2

{

   int a, b;

public:

   top(int i, int j) : base1(i*5), base2(j+i), a(i) { b = j;}

};

In dieser Klassenhierarchie würde eine Deklaration von top one(1, 2) die Initialisierung von base1 mit dem Wert 5 und von base2 mit dem Wert 3 ergeben. Die Initialisierungsmethoden lassen sich mischen.

Wie bereits oben erwähnt, werden die Basisklassen in der Reihenfolge ihrer Deklaration initialisiert. Anschließend erfolgt die Initialisierung der Elemente (ebenfalls in der Deklarationsreihenfolge), unabhängig von der Initialisierungsliste.

class X

{

  int a, b;

public:

  X(int i, j) :  a(i), b(a+j) {}

};

In dieser Klasse hat die Deklaration von X x(1,1) eine Zuweisung von 1 an x::a und von 2 an x::b zur Folge.

Basisklassen-Konstruktoren werden vor der Konstruktion der abgeleiteten Klassenelemente aufgerufen. Wenn sich die Werte der abgeleiteten Klasse ändern, hat dies keine Auswirkung auf die Erzeugung der Basisklasse.

class base

{

   int x;

public:

   base(int i) : x(i) {}

};

class derived : base

{

   int a;

public:

   derived(int i) : a(i*10), base(a) { } // Vorsicht! Der Basis wird ein

                                         // uninitialisiertes ‘a’ übergeben

};

Bei dieser Klassenkonstruktion führt der Aufruf eines derived d(1) nicht zum Wert 10 für das Basisklassen-Element x. Der an den Basisklassen-Konstruktor übergebene Wert ist undefiniert.

Wenn Sie für einen  Nicht-Inline-Konstruktor eine Initialisierer-Liste verwenden möchten, plazieren Sie die Liste nicht in die Klassendefinition, sondern an die Stelle, an der die Funktion definiert wird.

derived::derived(int i) : a(i)

{

  .

  .

  .

}


Destruktoren

Siehe auch

Der Destruktor einer Klasse wird aufgerufen, um Elemente eines Objekts freizugeben, bevor das Objekt selbst gelöscht wird. Der Destruktor ist eine Elementfunktion. Er trägt den Namen der Klasse mit einer führenden Tilde (~). Ein Destruktor akzeptiert weder Parameter, noch liefert er einen Rückgabetyp oder -wert.

#include <stdlib.h>

class X

{

public:

   ~X(){};  // Destruktor für Klasse X

};

Ist kein Destruktor explizit definiert, wird vom Compiler einer erzeugt.


Aufrufen von Destruktoren

Siehe auch

Ein Destruktor wird implizit aufgerufen, wenn eine Variable ihren deklarierten Gültigkeitsbereich verläßt. Destruktoren für lokale Variablen werden aufgerufen, wenn der Block, in dem sie deklariert sind, nicht mehr aktiv ist. Bei globalen Variablen werden Destruktoren als Teil der Exit-Prozedur nach main aufgerufen.

Wenn Zeiger auf Objekte ungültig werden, wird nicht implizit ein Destruktor aufgerufen. Zum Löschen eines derartigen Objekts muß demnach der delete-Operator aufgerufen werden.

Destruktoren werden in genau der umgekehrten Reihenfolge wie die zugehörigen Konstruktoren aufgerufen (siehe Aufrufreihenfolge von Konstruktoren).


atexit, #pragma exit und Destruktoren

Siehe auch

Globale Objekte sind aktiv, bis der Code in allen Exit-Prozeduren ausgeführt wurde. Lokale Variablen (auch die in main deklarierten) werden gelöscht, sobald sie den Gültigkeitsbereich verlassen. Die Ausführungsreihenfolge am Ende eines C++Builder Programms ist wie folgt:

 	atexit()-Funktionen werden in der Reihenfolge ausgeführt, in der sie auftreten.

 	#pragma exit-Funktionen werden in der Reihenfolge ihrer Prioritätsnummern ausgeführt.

 	Destruktoren für globale Variablen werden aufgerufen.


exit und Destruktoren

Siehe auch

Wird die exit-Routine innerhalb eines Programms aufgerufen, wird für keine lokale Variable im aktuellen Gültigkeitsbereich ein Destruktor aufgerufen. Globale Variablen werden in der Reihenfolge ihrer Deklaration gelöscht.


abort und Destruktoren

Siehe auch

Wird die abort-Routine in einem Programm aufgerufen, werden keine Destruktoren aktiviert. Dies gilt auch für Variablen mit globalem Gültigkeitsbereich.

Ein Destruktor kann auch explizit auf eine der folgenden Arten aufgerufen werden: indirekt durch einen Aufruf des Operators delete oder direkt durch Angabe des voll qualifizierten Destruktornamens. Mit delete können auch Objekte gelöscht werden, die mit new erzeugt wurden. Explizite Aufrufe von Destruktoren sind nur für Objekte nötig, die durch Aufrufe von new spezifische Adressen erhalten haben.

#include <stdlib.h>

class X {

public:

  .

  .

  .

   ~X(){};

  .

  .

  .

};

void* operator new(size_t size, void *ptr)

{

   return ptr;

}

char buffer[sizeof(X)];

void main() {

   X* pointer = new X;

   X* exact_pointer;

   exact_pointer = new(&buffer) X; // Zeiger wird mit der Pufferadresse

                                   // initialisiert.

  .

  .

  .

   delete pointer;                 // Mit delete wird der Zeiger gelöscht.

   exact_pointer–>X::~X();         // direkter Aufruf zur Speicherfreigabe

}


Virtuelle Destruktoren

Siehe auch

Ein Destruktor kann als virtual deklariert werden. Dies erlaubt es einem Zeiger auf ein Basisklassenobjekt, den richtigen Destruktor aufzurufen, wenn der Zeiger gerade auf ein abgeleitetes Klassenobjekt weist. Der Destruktor einer Klasse, die von einer Klasse mit einem virtuellen Destruktor abgeleitet ist, ist selbst virtuell.

/* Der folgende Programmteil demonstriert die Wirkung des virtual-Attributs auf

   die Reihenfolge der Destruktor-Aufrufe. Ohne die Deklaration eines virtuellen

   Destruktors in der Basisklasse würde der Destruktor der abgeleiteten Klasse
 nicht aufgerufen werden. */

#include <iostream.h>

class color {

public:

   virtual ~color() {  // virtueller Destruktor

      cout << "color dtor\n";

      }

};

class red : public color {

public:

   ~red() {  // Destruktor für Rot ist auch virtuell

      cout << "red dtor\n";

      }

};

class brightred : public red {

public:

   ~brightred() {  // Destruktor ist auch virtuell

       cout << "brightred dtor\n";

       }

};

int main() {

   color *palette[3];

   palette[0] = new red;

   palette[1] = new brightred;

   palette[2] = new color;

   // Die Destruktoren für red und color werden aufgerufen

   delete palette[0];

   cout << endl;

   // Die Destruktoren für bright red, red und color werden aufgerufen. Rot und Farbe

   delete palette[1];

   cout << endl;

   // Der Destruktor von color wird aufgerufen.

   delete palette[2];

   return 0;

}

Programmausgabe:

red dtor

color dtor

brightred dtor

red dtor

color dtor

color dtor

Wurden keine Destruktoren als virtuell deklariert, rufen delete palette[0], delete palette[1] und delete palette[2] nur den Destruktor für die Klasse color auf. Dadurch würden aber die ersten zwei Elemente, die in Wirklichkeit vom Typ red und brightred sind, nicht korrekt gelöscht.


Überladen von Operatoren

Siehe auch	Operatoren

In C++ können Sie die Funktion der meisten Operatoren neu definieren, so daß sie bei der Anwendung auf Objekte einer bestimmten Klasse eine spezielle Wirkung haben. Grundsätzlich unterscheidet der Compiler bei überladenen C++ Funktionen die verschiedenen Funktionen anhand des Kontexts des Aufrufs, also der Zahl und der Typen der Argumente oder Operanden.

Alle Operatoren können überladen werden, außer den folgenden:

.   .*   ::   ?:   #   ##

Die folgenden Präprozessor-Symbole können nicht überladen werden.

 #   ##

Die Operatoren =, [ ], ( ), und -> können nur als nichtstatische Elementfunktion überladen werden. Diese Operatoren können nicht von Typen enum überladen werden. Jeder Versuch, eine globale Version dieser Operatoren zu überladen, endet mit einem Compiler-Zeitfehler.

Das Schlüsselwort operator, gefolgt von dem Operatorsymbol, wird Operatorfunktionsname genannt; dieser kann wie ein normaler Funktionsname verwendet werden, wenn die neue (überladende) Funktion des Operators definiert wird.

Ein Funktionsoperator, der mit Argumenten aufgerufen wird, verhält sich wie ein Operator, der seine Operanden in einem Ausdruck verarbeitet. Eine Operatorfunktion kann weder die Anzahl der Argumente noch die Rangfolge- und Assoziativitätsregeln, wie sie für die normale Verwendung festgelegt sind, verändern.


Beispiel für das Überladen von Operatoren

Das folgende Beispiel erweitert die Klasse complex, um einen Vektor vom Typ complex zu erzeugen. Einige der nützlichsten Operatoren sind überladen, um die üblichen mathematischen Operationen in der gewohnten mathematischen Syntax zu ermöglichen.

Einige der Themen sind im folgenden Beispiel dargestellt:

Der Standardkonstruktor ist definiert. Das wird nur vom Compiler unterstützt, wenn Sie weder einen Standardkonstruktor noch einen anderen definiert haben.

Der Kopierkonstruktor ist explizit definiert. Normalerweise, wenn Sie keine Konstruktoren definiert haben, stellt Ihnen der Compiler einen zur Verfügung. Sie sollten den Kopierkonstuktor definieren, wenn Sie den Zuweisungsoperator überladen.

Der Zuweisungsoperator ist überladen. Wenn Sie den Zuweisungsoperator nicht überladen, ruft der Compiler den Standard-Zuweisungsoperator auf, wenn er benötigt wird. Durch das Überladen der Zuweisung vom Typ cvector legen Sie die auszuführenden Aktivitäten exakt fest. Beachten Sie, daß die Zuweisungsoperatorfunktion nicht von abgeleiteten Klassen geerbt werden kann.

Der Indexoperator ist als Elementfunktion (wird beim Überladen benötigt) mit einem einzigen Element definiert. Die Version vom Typ const stellt für den Aufrufer sicher, daß das Argument nicht verändert wird. Das ist beim beim Kopieren und Zuweisen nützlich. Der Operator sollt prüfen, daß sich der Indexwert innerhalb des gültigen Bereiches befindet. Hier ergibt sich eine gute Anwendungsmöglichkeit für die Ausnahmebehandlung.

Der Additionsoperator ist als eine Elementfunktion definiert. Er erlaubt das Addieren nur für die Typen cvector. Die Addition sollte immer überprüfen, ob die Größen der Operanden zueinander kompatibel sind.

Der Multiplikationsoperator ist als friend deklariert, was Ihnen die Definition der Anordnung der Operanden ermöglicht. Ein Versuch, die Anordnung der Operanden umzukehren, führt während des Compilierens zu einem Fehler.

Der Operator zu Einfügen von Streams ist überladen, um cvector auf eine natürliche Weise anzuzeigen. Große Objekte, die nicht gut auf einem Bildschirm mit begrenzter Größe angezeigt werden, könnten eine andere Strategie zum Anzeigen benötigen.

Quelltext

/* Wie man die Klasse complex erweitert und die benötigten Operatoren überlädt. */

#pragma warn -inl // IGNORIERE inline-WARNUNGEN, die nicht erweitert sind.

#include <complex.h>  // SCHLIESST iostream.h BEREITS MIT EIN

// KOMPLEXE VEKTOREN

class cvector {

   int size;

   complex *data;

public:

   cvector() { size = 0; data = NULL; };

   cvector(int i = 5) : size(i) {    // VOREINGESTELLTE GRÖßE VON VECTOR.

      data = new complex[size];

      for (int j = 0; j < size; j++)

           data[j] = j + (0.1 * j);  // WILLKÜRLICHE INITIALISIERUNG.

      };

   /* DIESE VERSION WIRD IN  main() AUFGERUFEN */

   complex& operator [](int i) { return data[i]; };

   /* DIESE VERSION WIRD UM ZUWEISUNGSOPERATOR AUFGERUFEN UND KOPIERT DEN KONSTRUKTOR */

   const complex& operator [](int i) const { return data[i]; };

   cvector operator +(cvector& A) {  // ADDITIONSOPERATOR

      cvector result(A.size);  // ÄNDERT NICHT DAS ORIGINAL

      for (int i = 0; i < size; i++)

          result[i] = data[i] + A.data[i];

      return result;

      };

   /* WEIL DIE MULTIPLIKATION * vector MULTIPLICATION NICHT KOMMUNIKATIV

      IST, MUß DIE ANORDNUNG DER ELEMENTE FESTGELEGT WERDEN. DIESER

      FRIEND-OPERATOR STELLT DIE RICHTIGE MULTIPLIKATION SICHER. */

   friend cvector operator *(int scalar, cvector& A) {

      cvector result(A.size);  // ÄNDERT NICHT DAS ORIGINAL

      for (int i = 0; i < A.size; i++)

        result.data[i] = scalar * A.data[i];

      return result;

      }

   /* DER OPERATOR ZUM EINFÜGEN VON STREAMS. */

   friend ostream& operator <<(ostream& out_data, cvector& C) {

      for (int i = 0; i < C.size; i++)

           out_data << "[" << i << "]=" << C.data[i] << "   ";

      cout << endl;

      return out_data;

      };

   cvector( const cvector &C ) {  // KOPIERKONSTRUCTOR

      size = C.size;

      data = new complex[size];

      for (int i = 0; i < size; i++)

           data[i] = C[i];

     }

   cvector& operator =(const cvector &C) { // ZUWEISUNGSOPERATOR.

      if (this == &C) return *this;

      delete[] data;

      size = C.size;

      data = new complex[size];

      for (int i = 0; i < size; i++)

          data[i] = C[i];

      return *this;

   };

   virtual ~cvector() { delete[] data; }; // DESTRUKTOR

   };

int main(void) { /* EIN PAAR OPERATIONEN MIT complex VEKTOREN. */

   cvector cvector1(4), cvector2(4), result(4);

   // ERZEUGT ZAHLEN VOM Typ complex UND WEIST SIE AN VEKTOREN VOM Typ

   // complex ZU.

   cvector1[3] = complex(3.3, 102.8);

   cout << "Hier ist cvector1:" << endl;

   cout << cvector1;

   cvector2[3] = complex(33.3, 81);

   cout << "Hier ist cvector2:" << endl;

   cout << cvector2;

   result = cvector1 + cvector2;

   cout << "Das Ergebnis der Vektoraddition:" << endl;

   cout << result;

   result = 10 * cvector2;

   cout << "Das Ergebnis von 10 * cvector2:" << endl;

   cout << result;

   return 0;

   }

Ausgabe

Hier ist  cvector1:

[0]=(0, 0)   [1]=(1.1, 0)   [2]=(2.2, 0)   [3]=(3.3, 102.8)

Hier ist  cvector2:

[0]=(0, 0)   [1]=(1.1, 0)   [2]=(2.2, 0)   [3]=(33.3, 81)

Das Ergebnis der Vektoraddition:

[0]=(0, 0)   [1]=(2.2, 0)   [2]=(4.4, 0)   [3]=(36.6, 183.8)

Das Ergebnis von  10 * cvector2:

[0]=(0, 0)   [1]=(11, 0)   [2]=(22, 0)   [3]=(333, 810)


Überladen von Operatorfunktionen

Siehe auch	Operatoren

Operatorfunktionen können direkt aufgerufen werden, aber im Normalfall werden sie indirekt unter Verwendung des Überladungsoperators aufgerufen.

c3 = c1.operator + (c2);   // gleichwertig mit c3 = c1 + c2

Mit Ausnahme der Operatoren new und delete , für die eigene Regeln gelten, muß eine Operatorfunktion entweder eine nichtstatische Elementfunktion sein oder mindestens ein Argument vom Klassentyp haben. Die Operatorfunktionen =, ( ), [ ] und -> müssen nichtstatische Elementfunktionen sein.

Aufzählungen können überladene Operatoren haben. Jedoch können die Operatorfunktionen =, ( ), [ ] und -> nicht überladen werden für Aufzählungen.


Überladene Operatoren und Vererbung

Siehe auch	Operatoren

Mit Ausnahme des Zuweisungsfunktionsoperators =( ) werden alle überladenen Operatorfunktionen für die Klasse X an die von X abgeleiteten Klassen vererbt. Dabei gelten die gleichen standardmäßigen Auflösungsregeln wie für überladene Funktionen. Wenn X eine Basisklasse für Y ist, kann eine überladene Operatorfunktion für X möglicherweise nochmals für Y überladen werden.


Überladen unärer Operatoren
Überladen von unären Operatoren

Siehe auch	Operatoren

Ein unärer Präfix- oder Suffixoperator kann auf zweierlei Arten überladen werden: durch die Deklarierung einer nichtstatischen Elementfunktion ohne Argumente oder durch die Deklarierung einer Nicht-Elementfunktion mit einem Argument. Wenn @ für einen unären Operator steht, können @x und x@ sowohl als x.operator@() als auch als operator@(x) interpretiert werden (abhängig von den vorangegangenen Deklarationen). Wenn beide Formen deklariert wurden, wird der standardmäßige Argumentvergleich vorgenommen, um die Doppeldeutigkeit zu beseitigen.

In der Version 2.0 von C++ wird der überladene Operator ++ oder -- sowohl als Präfix- als auch als Suffixoperator verwendet.

Wenn in der Version 2.1 von C++ operator++ oder operator -- als Elementfunktionen ohne Parameter oder als Nicht-Elementfunktion mit einem Parameter deklariert ist, so wird nur der Präfixoperator ++ bzw. -- überladen. Den Suffixoperator ++ oder -- können Sie nur überladen, indem Sie ihn als Elementfunktion mit einem int-Parameter oder als Nicht-Elementfunktion mit einem Klassenargument und einem int-Argument definieren.

Wenn nur die Präfixversion des Operators ++ oder -- überladen ist und der Operator auf ein Klassenobjekt als Suffixoperator angewendet wird, so gibt der Compiler eine Warnung aus. Anschließend ruft er den Präfixoperator auf, so daß das Programm wie ein der Version 2.0 entsprechendes Programm compiliert wird. In diesem Fall werden folgende Warnungen ausgegeben:

Warning: Overloaded prefix 'operator ++' used as a postfix operator in function func()

Warning: Overloaded prefix 'operator --' used as a postfix operator in function func()


Überladen binärer Operatoren
Überladen von binären Operatoren

Siehe auch	Operatoren

Ein binärer Operator kann auf zwei Arten überladen werden: durch die Deklarierung einer nichtstatischen Elementfunktion mit einem Argument oder durch die Deklarierung einer Nicht-Elementfunktion (normalerweise als friend) mit zwei Argumenten. Wenn @ für einen binären Operator steht, kann x@y sowohl als x.operator@(y) als auch als operator@(x,y) interpretiert werden (abhängig von den vorangegangenen Deklarationen). Wenn beide Formen deklariert wurden, wird der standardmäßige Argumentvergleich vorgenommen, um die Doppeldeutigkeit zu beseitigen.


Überladen des Zuweisungsoperators  =

Siehe auch	Operatoren

Der Zuweisungsoperator = kann überladen werden, indem man eine nichtstatische Elementfunktion deklariert. Ein Beispiel dafür ist:

class String {

     .

     .

     .

   String& operator = (String& str);

     .

     .

     .

   String (String&);

   ~String();

}

Das obige Programmstück ermöglicht es (zusammen mit passenden Definitionen von String::operator =()), Stringzuweisungen wie in anderen Sprachen in der Form str1 = str2 zu schreiben. Im Gegensatz zu den anderen Operatorfunktionen kann die Zuweisungsoperatorfunktion nicht von abgeleiteten Klassen geerbt werden.Gibt es für eine bestimmte  Klasse X keinen anwenderdefinierten Operator =, so wird der Operator = standardmäßig als Element-zu-Element-Zuweisung der Elemente der Klasse X definiert:

X& X::operator = (const X& source)

{

   // elementweise Zuweisung

}


Überladen des Funktiosaufruf-Operators  ( )
Überladen des Funktionsaufrufoperators ( )

Siehe auch	Operatoren

Syntax

postfix-ausdruck ( <ausdrucksliste> )

Beschreibung

Beim gewöhnlichen Gebrauch als Funktionsaufruf muß der Postfix-Ausdruck ein Funktionsname, ein Zeiger auf eine Funktion oder eine Funktionsreferenz sein. Soll der Aufruf einer Elementfunktion gemeint sein, muß der Postfix-Ausdruck der Name einer (Klassen-)Elementfunktion sein bzw. ein Zeiger auf ein Element, von dem eine (Klassen-)Elementfunktion bestimmt werden kann. In beiden Fällen folgt auf den Postfix-Ausdruck die optionale Liste von Ausdrücken.

Der Aufruf x(arg1, arg2), wobei x ein Objekt der Klasse X ist, wird interpretiert als x.operator()(arg1,arg2).

Die Operatorfunktion operator()() kann vom Anwender nur mit Hilfe einer nichtstatischen Elementfunktion für eine Klasse X (und alle abgeleiteten Klassen) definiert werden.


Überladen des Indizierungsoperators  [ ]

Syntax

postfix-ausdruck [ ausdruck ]

Beschreibung

Die entsprechende Operatorfunktion ist operator[](). Sie kann vom Benutzer nur mit Hilfe einer nichtstatischen Elementfunktion definiert werden für eine Klasse X (und jede davon abgeleitete Klasse). Der Ausdruck x[y], wobei x ein Objekt der Klasse X ist, wird interpretiert als x.operator[](y).

operator[]() kann nur als nicht-statische Mitgliedsfunktion überladen werden.


Überladen des Klassenelementzugriffs-Operators ->
Überladen des Klassenelementzugriffsoperators  ->

Siehe auch	Operatoren

Syntax

postfix-expression -> primary-expression

Beschreibung

Der Ausdruck x->m, wobei x ein Objekt der Klasse X ist, wird interpretiert als (x.operator->())->m. Die Funktion operator->() muß daher entweder einen Zeiger auf ein Klassenobjekt oder ein Objekt einer Klasse, für die operator-> definiert ist, zurückliefern. Sie kann vom Benutzer nur mit Hilfe einer nichtstatischen Elementfunktion definiert werden.

operator->() kann nur als nicht-statische Mitgliedsfunktion überladen werden.


Polymorphe Klassen

Siehe auch

Klassen, die identische Schnittstellen besitzen, sich aber so implementieren lassen, daß sie unterschiedliche spezielle Anforderungen erfüllen, werden als polymorphe Klassen bezeichnet.  Eine Klasse ist polymorph, wenn sie mindestens eine virtuelle (oder abstrakte) Funktion deklariert oder erbt. Die einzigen Typen, die Polymorphie unterstützen, sind class und struct.


Virtuelle Funktionen

Siehe auch

Mit Hilfe virtueller Funktionen können abgeleitete Klassen spezifische Versionen einer Basisklassen-Funktion bereitstellen. Eine virtuelle Funktion wird in der Basisklasse mit dem Schlüsselwort virtual deklariert. Um eine abstrakte Funktion zu deklarieren (was automatisch eine abstrakte Klasse deklariert), stellen Sie vor den Prototyp das Schlüsselwort virtual und setzen die Funktion auf Null.

virtual int funct1(void);       // virtuelle Funktionsdeklaration

virtual int funct2(void) = 0;   // abstrakte Funktionsdeklaration

virtual void funct3(void) = 0 { // Diese Deklaration ist zulässig.

   // Hier steht beliebiger Programmcode.

   };

Hinweis:	Abstrakte virtuelle Funktionen werden unter Abstrakte Klassen behandelt.

Wenn Sie virtual-Funktionen deklarieren, beachten Sie bitte folgende Regeln:

 	Es darf sich nur um Elementfunktionen handeln.

 	Sie können als friend einer anderen Klasse deklariert sein.

 	Sie können keine statischen Elemente sein.

Eine virtual-Funktion muß in einer abgeleiteten Klasse nicht neu definiert werden. Sie können eine Definition in der Basisklasse bereitstellen, so daß alle Aufrufe auf die Basisfunktion zugreifen.

Um eine virtual-Funktion in einer beliebigen abgeleiteten Klasse neu zu definieren, müssen Anzahl und Typ der Argumente in der Basisklassen-Deklaration und in der Deklaration in der abgeleiteten Klasse identisch sein. (Der Fall, in dem neu definierte virtual-Funktionen sich nur im Rückgabetyp unterscheiden, wird weiter unten behandelt.) Von einer neu definierten Funktion wird gesagt, daß sie die Basisklassenfunktion überschreibt.

Sie können die Funktionen int Base::Fun(int) und int Derived::Fun(int) selbst dann deklarieren, wenn sie nicht virtual sind. In diesem Fall wird von int Derived::Fun(int) gesagt, daß es andere Versionen von Fun(int), die in einer beliebigen Basisklasse existieren, verdeckt. Wenn außerdem die Klasse Derived andere Versionen von Fun() definiert (d.h. Versionen von Fun() mit unterschiedlichen Funktionsköpfen), werden solche Versionen überladene Versionen von Fun() genannt.

Rückgabetypen von virtuellen Funktionen

Wenn eine virtual-Funktion neu definiert wird, kann nicht einfach nur ihr Rückgabetyp geändert werden. Um sie korrekt neu zu definieren, muß die neue Definition (in einer abgeleiteten Klasse) genau mit dem Rückgabetyp und den formalen Parametern der ursprünglichen Deklaration übereinstimmen. Wenn zwei Funktionen mit demselben Namen unterschiedliche formale Parameter besitzen, sieht sie C++ als verschieden an, der bei virtual-Funktionen ablaufende Mechanismus greift nicht.

Bestimmte virtual-Funktionen in einer Basisklasse können jedoch in einer abgeleiteten Klasse eine überschreibende Version mit einem Rückgabetyp besitzen, der von dem der überschriebenen Funktion abweicht. Dies ist nur möglich, wenn beide der folgenden Bedingungen erfüllt sind:

 	Die überschriebene virtual-Funktion gibt einen Zeiger oder eine Referenz auf die Basisklasse zurück.

 	Die überschreibende Funktion gibt einen Zeiger oder eine Referenz auf die abgeleitete Klasse zurück.

Enthält sowohl die Basisklasse B als auch die Klasse D (als public von B abgeleitet) eine virtual-Funktion vf, und wird vf für ein Objekt d von D aufgerufen, erfolgt der Aufruf D::vf() auch dann, wenn der Zugriff über einen Zeiger oder eine Referenz auf B erfolgt. Ein Beispiel:

struct X {};	// Basisklasse

struct Y : X {};	// Abgeleitete°Klasse

struct B {

   virtual void vf1();

   virtual void vf2();

   virtual void vf3();

   void f();

   virtual X* pf();	// Rückgabetyp ist ein Zeiger auf die Basis.

  	// Kann überschrieben werden.

   };

class D : public B {

public:

   virtual void vf1();	// Virtual-Spezifizierer ist zulässig, aber nicht notwendig.

   void vf2(int);	      // nicht virtuell, da andere Argumentliste verwendet wird.

                       // dadurch wird B::vf2() verdeckt.

// char vf3();	// Illegal: nur Rückgabetyp geändert!

   void f();

   Y*  pf();	// Überschreibende Funktion unterscheidet sich

  	// nur im Rückgabetyp. Gibt Zeiger auf

    // die abgeleitete Klasse zurück.

   };

void extf() {

   D d;	// D instanziieren

   B* bp = &d;	// Standardkonvertierung von D* in B*

  	// bp mit Funktionstabelle initialisieren, die

// für Objekt d zur Verfügung steht. Wenn in

  	// der d-Tabelle kein Eintrag für eine Funktion

     	// steht, wird Funktion in B-Tabelle verwendet.

   bp–>vf1();  // Ruft D::vf1 auf

   bp–>vf2();  // Ruft B::vf2 auf, da D::vf2 andere Argumente hat

   bp–>f();    // Ruft B::f auf (nicht virtuell)

   X* xptr = bp–>pf();   // Ruft D::pf() auf und konvertiert

   	// Ergebnis in einen Zeiger auf X.

   D* dptr = &d;

   Y* yptr = dptr–>pf();	// Ruft D::pf() auf und initialisiert yptr.

   	// Es erfolgt keine weitere Konvertierung.

   }

Die überschreibende Funktion vf1 in D ist automatisch virtual. Der Spezifizierer virtual darf in der abgeleiteten Klasse mit einer überschreibenden Funktionsdeklaration verwendet werden. Wenn von D andere Klassen abgeleitet werden, ist das Schlüsselwort virtual erforderlich. Werden von D keine weiteren Klassen abgeleitet,  ist der Gebrauch von virtual überflüssig.

Die Interpretation einer virtual-Funktion hängt vom Typ des Objekts ab, für das der Aufruf erfolgt. Bei nicht-virtuellen Funktionsaufrufen ist die Interpretation nur vom Typ des Zeigers oder der Referenz abhängig, der bzw. die das betreffende Objekt bezeichnet.

Virtual-Funktionen zahlen aber einen Preis für ihre Vielseitigkeit: Jedes Objekt in der abgeleiteten Klasse braucht einen Zeiger auf eine Tabelle mit virtuellen Funktionen, damit zur Laufzeit die richtige Funktion gewählt wird (späte Bindung).


Abstrakte Klassen

Siehe auch

Eine abstrakte Klasse ist eine Klasse mit mindestens einer abstrakten virtual-Funktion. Eine virtual-Funktion wird zu einer abstrakten Funktion, indem sie auf Null gesetzt wird.

Eine abstrakte Klasse kann nur als eine Basisklasse für andere Klassen verwendet werden. Es ist nicht möglich, Objekte einer abstrakten Klasse zu generieren. Eine abstrakte Klasse kann nicht als Argumenttyp oder Funktionsrückgabetyp verwendet werden. Es ist jedoch möglich, Zeiger auf eine abstrakte Klasse zu deklarieren. Wenn zur Initialisierung kein temporäres Objekt benötigt wird, sind auch Referenzen auf eine abstrakte Klasse erlaubt. Dazu ein Beispiel:

class shape {       // abstrakte Klasse

   point center;

        .

        .

        .

public:

   where() { return center; }

   move(point p) { center = p; draw(); }

   virtual void rotate(int) = 0; // abstrakte virtuelle Funktion

   virtual void draw() = 0;      // abstrakte virtuelle Funktion

   virtual void hilite() = 0;    // abstrakte virtuelle Funktion

       .

       .

       .

}

shape x;	// FEHLER: Versuch, ein Objekt einer abstrakten Klasse zu erzeugen

   shape* sptr;	// Zeiger auf abstrakte Klasse ist zulässig

   shape f();	// FEHLER: abstrakte Klasse kann kein Rückgabetyp sein

int g(shape°s);	// FEHLER: abstrakte Klasse kann kein Funktionsargumenttyp sein

shape& h(shape&);	// Referenz auf abstrakte Klasse als Rückgabewert

 	// oder Funktionsargument ist OK

Nehmen wir an, D sei eine abgeleitete Klasse mit der abstrakten Klasse B als unmittelbarer Basisklasse, die eine abstrakte Funktion pvf enthält. Stellt D keine Definition für pvf bereit, wird pvf eine abstrakte Elementfunktion von D, und D wird dadurch zu einer abstrakten Klasse.

Mit der oben eingeführten Klasse shape sieht das folgendermaßen aus:

class circle : public shape {	// circle abgeleitet von abstrakter Klasse

   int radius;	            // private

public:

   void rotate(int) { }	      // definiert virtuelle Funktion: zur Rotation eines

                        	// Kreises wird keine Aktion benötigt.

   void draw();         	// circle::draw an anderer Stelle definiert werden

}

Elementfunktionen können von einem Konstruktor einer abstrakten Klasse aufgerufen werden. Der direkte oder indirekte Aufruf einer abstrakten Funktion führt jedoch zu einem Laufzeitfehler.


Gültigkeitsbereich in C++

Siehe auch

Die lexikalischen Gültigkeitsbereichsregeln für C++ folgen mit Ausnahme des Klassen-Gültigkeitsbereichs den generellen C-Regeln, mit dem Unterschied, daß C++, anders als C, Daten- und Funktionsdeklarationen an jeder Stelle zuläßt, an der auch Anweisungen möglich sind. Diese Flexibilität erfordert aber Vorsicht bei der Interpretation der Begriffe "einschließender Gültigkeitsbereich" und "Deklarationspunkt".


Gültigkeitsbereich Klasse

Siehe auch

Der Name M eines Elements der Klasse X hat den Gültigkeitsbereich Klasse "lokal zu X" und kann nur in den folgenden Fällen verwendet werden:

 	In Elementfunktionen von X.

 	In Ausdrücken wie x.M, wobei X ein Objekt von X ist.

 	In Ausdrücken wie xptr->M, wobei xptr ein Zeiger auf ein Objekt von X ist.

 	In Ausdrücken wie X::M oder D::M, wobei D eine abgeleitete Klasse von X ist.

 	In Vorwärtsreferenzen innerhalb der Klasse, in der M ein Element ist.

Namen von Funktionen, die als friend von X deklariert wurden, sind keine Elemente von X, ihre Namen haben nur einschließenden Gültigkeitsbereich.


Verbergen

Siehe auch

Ein Name kann durch eine explizite Deklaration desselben Namens in einem eingeschlossenen Block oder einer Klasse verborgen werden. Mit dem Zugriffsoperator und dem Klassennamen kann aber immer noch auf ein verborgenes Klassenelement zugegriffen werden: X::M. Ein verborgener (globaler) Name mit Gültigkeitsbereich-Datei kann mit dem unären Operator :: referenziert werden (z.B. ::g). Ein Klassenname X kann durch den Namen eines Objekts, einer Funktion oder eines Aufzählungstyps innerhalb des Gültigkeitsbereichs von X verborgen werden, unabhängig von der Reihenfolge, in der die Namen deklariert sind. Auf den verborgenen Klassennamen kann durch Voranstellen des geeigneten Schlüsselworts (class, struct oder union) weiterhin zugegriffen werden.

Der Deklarationspunkt für einen Namen x liegt unmittelbar hinter seiner vollständigen Deklaration, aber vor dem Initialisierer (falls vorhanden).


Zusammenfassung der C++ Gültigkeitsbereichsregeln

Siehe auch

Die folgenden Regeln betreffen alle Namen, einschließlich der typedef- und Klassennamen, vorausgesetzt, daß C++ derartige Namen im jeweiligen Zusammenhang erlaubt:

 	Der Name selbst wird auf Mehrdeutigkeit hin überprüft. Werden keine Mehrdeutigkeiten innerhalb des Gültigkeitsbereichs festgestellt, wird die Zugriffssequenz eingeleitet.

 	Wenn kein Zugriffsfehler auftritt, wird der Typ des Objekts, der Funktion, der Klasse, von typedef usw. überprüft.

 	Wenn der Name außerhalb der Funktion oder Klasse verwendet wird oder den unären Zugriffsoperator :: als Präfix hat, und wenn der Name nicht durch den binären Operator :: oder die Elementauswahloperatoren . und -> qualifiziert ist, muß der Typ ein globales Objekt, eine globale Funktion oder einen globalen Aufzählungstyp bezeichnen.

 	Erscheint der Name n in der Form X::n, x.n (wobei x eine Instanz von X oder eine Referenz auf X ist) oder ptr->n (wobei ptr ein Zeiger auf X ist), dann ist n der Name für ein Element von X oder ein Element einer Basisklasse von X.

 	Jeder Name, auf den die bisherigen Beschreibungen nicht zutreffen und der in einer statischen Elementfunktion verwendet wird, muß in dem Block, in den er gehört, oder in einem einschließenden Block deklariert werden, oder er muß ein globaler Name sein. Die Deklaration eines lokalen Namens n verbirgt Deklarationen von n in einschließenden Blöcken und in globalen Deklarationen von n. Namen in unterschiedlichen Gültigkeitsbereichen werden nicht überladen.

 	Jeder Name, auf den die bisherigen Beschreibungen nicht zutreffen und der in einer nicht-statischen Elementfunktion der Klasse X verwendet wird, muß in dem Block, in den er gehört, oder in einem einschließenden Block deklariert werden, oder er muß ein Element der Klasse X bzw. einer Basisklasse von X oder ein globaler Name sein. Die Deklaration eines lokalen Namens n verbirgt Deklarationen von n in einschließenden Blöcken, in Elementfunktionen der Klasse und in globalen Deklarationen von n. Die Deklaration eines Elementnamens verbirgt Deklarationen mit demselben Namen in Basisklassen.

 	Der Name eines Funktionsarguments in einer Funktionsdefinition liegt im Gültigkeitsbereich des äußersten Blocks der Funktion. Der Name eines Funktionsarguments in einer nicht-definierenden Funktionsdeklaration hat überhaupt keinen Gültigkeitsbereich. Der Gültigkeitsbereich eines Standardarguments wird durch seinen Deklarationspunkt festgelegt, Ausdrücke für den Standardwert können jedoch nicht auf lokale Variablen oder auf nicht-statische Klassenelemente zugreifen. Standardargumente werden an jeder Aufrufposition ausgewertet.

 	Ein Konstruktor-Initialisierer (siehe ctor-Initialisierer in C++Builder Deklarationssyntax.) wird im Gültigkeitsbereich des äußersten Blocks des Konstruktors ausgewertet, so daß er sich auf den Namen der Konstruktorargumente beziehen kann.


Benutzen von Templates

Siehe auch

Mit Templates, die auch generische bzw. parametrisierte Typen genannt werden, können Sie eine Familie gleichartiger Funktionen oder Klassen aufbauen. Folgende Themen führen in die grundlegenden Konzepte von Templates ein:

Exportieren und Importieren von Templates

Die Syntax von Templates

Zerlegungen im Rumpf von Templates

Funktions-Templates

Klassen-Templates

Implizite and explizite Template-Funktionen

Compiler-Optionen für Templates


template (Schlüsselwort)
template

Siehe auch	Schlüsselwort

Syntax

template-declaration:

   template < template-argument-liste > deklaration

template-argument-liste:

   template-argument

   template-argument-liste, template argument

template-argument:

   type-argument

   argument-deklaration

typ-argument:

   class Bezeichner

template-klassen-name:

   template-name < template-argument-liste >

template-argument-liste:

   template-argument

   template-argument-liste , template-argument

template-argument:

   ausdruck

   typ-name

< template-argument-liste > deklaration

Beschreibung

Templates, auch "generische" oder "parametrisierte" Typen genannt, dienen zur Erzeugung einer Familie verwandter Funktionen oder Klassen.


Zerlegungen im Rumpf von Templates

Siehe auch

Frühere Versionen des Compilers prüften die Syntax im Rumpf von Template nicht, bis das Template instantiiert wurde. Ein Rumpf eines Templates wird nun sofort zerlegt, wenn er wie jede andere Deklaration sichtbar ist.

template <class T> class X : T

{

  Int  j;  // Fehler: Typname, der im Template X<T> erwartet wird

};

Lassen Sie uns annehmen, daß Int bislang nicht definiert wurde. Das bedeutet, daß Int ein Element vom Argument T des Templates sein muß. Es könnte aber auch einfach ein Tippfehler sein, so daß int anstatt Int gemeint war. Weil der Compiler die richtige Bedeutung nicht erraten kann, gibt er eine Fehlermeldung aus.

Wenn Sei auf Typen zugreifen wollen, die als Argument eines Templates definiert sind, sollten Sie typedef verwenden, um dem Compiler Ihre Intention unmißverständlich mitzuteilen:

template <class T> class X : T

{

  typedef  T::Int  Int;

  Int  j;

};

Sie dürfen nicht einfach

   typedef   T::Int;

schreiben, so wie in früheren Versionen des Compilers. Dort wurde es akzeptiert, den typedef-Namen nicht zu liefern, was nun eine Fehlermeldung verursacht.

Alle anderen im Rumpf des Templates erwähnten Templates sind zu diesem Zeitpunkt definiert oder deklariert. Deshalt ist das folgende Beispiel schlecht gemacht und wird nicht compilieren:

template <class T> class  X

{

  void f(NotYetDefindedTemplate<T> x);

};

Alle Template-Definitionen müssen mit einem Semikolon enden. Frühere Versionen des Compilers akzeptierten es, wenn der Strichpunkt fehlte.


Funktions-Templates

Siehe auch	Templates

Sehen wir uns die Funktion max(x, y) an, die das größere der beiden Argumente zurückgibt. x und y können von einem beliebigen, sortierbaren Typ sein. Da C++ eine streng auf Typen ausgerichtete Sprache ist, müssen die Argumenttypen zum Zeitpunkt der Compilierung deklariert werden. Ohne Templates sind viele überladene Versionen von max erforderlich, d.h., eine Version für jeden unterstützten Datentyp, obwohl der Programmcode im Prinzip für jede Version identisch ist. Jede Version vergleicht die Argumente miteinander und gibt das größere zurück.

Um dieses Problem zu umgehen, kann man z.B. ein Makro verwenden:

#define max(x,y) ((x > y) ? x : y)

Die Verwendung von #define umgeht jedoch die Typüberprüfung, die ein wesentlicher Vorteil von C++ gegenüber C ist. Der Einsatz von Makros ist in C++ nahezu überflüssig. Die Aufgabe von max(x, y) liegt im Vergleichen kompatibler Typen. Das Makro seinerseits erlaubt jedoch leider auch einen Vergleich von int und struct Typen, die nicht kompatibel sind.

Ein weiteres Problem bei der Verwendung des Makros liegt darin, daß eine Substitution ausgeführt wird, wo dies gar nicht erwünscht ist. Wenn Sie statt dessen ein Template verwenden, können Sie eine Maske für gleichartige überladene Funktionen definieren, indem Sie den Datentyp selbst als Parameter verwenden:

template <class T> T max(T x, T y){

   return (x > y) ? x : y;

   };

Der Datentyp wird durch das Template-Argument <class T> repräsentiert. Beim Einsatz in einer Anwendung generiert der Compiler den geeigneten Code für die max Funktion gemäß dem im Aufruf tatsächlich verwendeten Datentyp:

int i;

Myclass a, b;

int j = max(i,0);       // Integer-Argumente

Myclass m = max(a,b);   // Argumente vom Typ Myclass

Für <class T>  kann jeder beliebige Datentyp (nicht nur Klassen) verwendet werden. Der Compiler ruft dann den entsprechenden operator>() auf, so daß Sie max mit Argumenten jedes beliebigen Typs einsetzen können, für den der operator>() definiert ist.


Überschreiben einer Template-Funktion

Templates

Das vorhergehende Beispiel wird Funktions-Template (oder auch generische Funktion) genannt. Eine spezielle Instanzerzeugung des Funktions-Template heißt Template-Funktion. Die Instanzerzeugung einer Template-Funktion erfolgt, wenn Sie die Funktionsadresse übernehmen oder die Funktion mit definierten (d.h. nicht-generischen) Datentypen aufrufen. Sie können die Generierung einer Template-Funktion für einen speziellen Typ mit einer Nicht-Template-Funktion überschreiben:

#include <string.h>

char *max(char *x, char *y){

   return(strcmp(x,y) > 0) ? x : y;

}

Wenn Sie die Funktion mit String-Argumenten aufrufen, wird sie anstelle der automatischen Template-Funktion ausgeführt. In diesem Fall verhinderte der Aufruf der Funktion einen sinnlosen Vergleich zwischen zwei Zeigern.

Nur einfache Argument-Konvertierungen werden mit Template-Funtionen ausgeführt, die vom Compiler genriert wurden.

Die Argumenttypen einer Template-Funktion müssen alle formalen Argumente des Template abdecken. Wenn dies nicht erfolgt, gibt es keine Möglichkeit, beim Aufruf der Funktion die tatsächlichen Werte des nicht verwendeten Template-Argumente zu ermitteln.


Implizite und explizite Template-Funktionen

Templates

Bei der Auflösung von Aufrufen überladener Funktionen, (den Schritten für die Suche nach einer genauen Übereinstimmung folgend), ignoriert der Compiler Template-Funktionen, die implizit von ihm selbst erzeugt wurden.

template<class T> T max(T a, T b){

        return  (a > b) ? a : b;

};

void    f(int i, char c){

        max(i, i);              // ruft max(int ,int ) auf

        max(c, c);              // ruft max(char,char) auf

        max(i, c);              // keine Übereinstimmung für max(int,char)

        max(c, i);              // keine Übereinstimmung für max(char,int)

}

Dieser Programmcode gibt folgende Fehlermeldungen aus:

Could not find a match for 'max(int,char)' in function f(int,char)

Could not find a match for 'max(char,int)' in function f(int,char)

Wenn der Anwender eine Template-Funktion jedoch explizit deklariert, wird die Funktion vollständig in die Suche nach der besten Übereinstimmung einbezogen: Siehe das Beispiel der expliziten Template-Funktion.

Bei der Suche nach einer exakten Übereinstimmung der Funktionsparameter der Templates, werden einfache Umwandlungen als exakte Übereinstimmungen betrachtet. Siehe das Beispiel für einfache Umwandlungen.

Template-Funktionen mit abgeleiteten Klassenzeigern oder Referenzargumenten stimmen mit ihren öffentlichen Basisklassen überein. Siehe das Beispiel der Referenzierung von Basisklassen.


Klassen-Templates

Siehe auch	Using Templates	Beispiel

Mit einem Klassen-Template (auch generische Klasse oder Klassengenerator genannt) können Sie eine Maske für Klassendefinitionen erstellen. Sehen Sie sich das folgende Beispiel einer Vektorklasse an (ein eindimensionales Array). Die grundlegenden Operationen (Einfügen, Löschen, Indizieren usw.), die für den Typ ausgeführt werden, sind immer dieselben, unabhängig davon, ob Sie einen Vektor mit Integerwerten oder mit einem anderen Typ haben. Wenn der Elementtyp als Parameter Typ für die Klasse behandelt wird, erzeugt das System typsichere Klassendefinitionen:

Wie bei Funktions-Templates kann eine explizite Definition von Template-Klassen die automatische Definition eines gegebenen Typs überschreiben:

class Vector<char *> { ... };

Dem Symbol Vector muß immer ein Datentyp in spitzen Klammern zugeordnet werden. Es kann nicht alleine stehen; ausgenommen sind nur einige Fälle in der ursprünglichen Template-Definition.


Template-Argumente

Templates

Die obigen Beispiele verwenden alle nur ein Template-Argument. Es sind aber mehrere Argumente erlaubt. Template-Argumente können zusätzlich zu den Datentypen auch Werte repräsentieren:

template<class T, int size = 64> class Buffer { ... };

Argumente, die kein Template-Typ sind, wie beispielsweise size, können Standardwerte haben. Der Wert, der für ein solches Argument angegeben werden kann, muß ein konstanter Ausdruck sein:

const int N = 128;

int i = 256;

Buffer<int, 2*N> b1;// OK

Buffer<float, i> b2;// Fehler: i ist keine Konstante

Da jede Instanziierung eines Klassen-Template eine Klasse ist, erhält sie ihre eigene Kopie der statischen Elemente, so wie auch Template-Funktionen ihre eigene Kopie der statischen lokalen Variablen erhalten.


Verwenden von spitzen Klammern in Templates

Templates

Seien Sie vorsichtig bei der Verwendung der rechten spitzen Klammer bei der Instanziierung:

Buffer<char, (x > 100 ? 1024 : 64)> buf;

Im obigen Beispiel würde die spitze Klammer zwischen x und 100 die Template-Argumentliste vorzeitig schließen, wenn das zweite Argument nicht in runden Klammern stünde.


Verwenden von typsicheren generische Listen als Template
Verwenden von typsicheren generischen Listen in Templates

Templates

Wenn Sie häufig  nahezu identische Programmcodes schreiben, sollten Sie immer an die Verwendung von Templates denken. Bei der folgenden Klassendefinition (einer generischen Listenklasse) liegt das Problem darin, daß sie nicht typsicher ist.

class GList

{

 public:

   void insert( void * );

   void *peek();

    .

    .

    .

};

Üblicherweise werden zur Lösung dieses Problems wiederholte Klassendefinitionen verwendet. Da für die Einfügungen keine Typüberprüfung erfolgt, haben Sie keine Möglichkeit zu erfahren, welches Ergebnis Sie erhalten. Sie können dieses Problem mit Hilfe einer umschließenden Klasse beheben:

class FooList : public Glist {

 public:

   void insert( Foo *f ) { GList::insert( f ); }

   Foo *peek() { return (Foo *)GList::peek(); }

      .

      .

      .

};

Damit ist die Typsicherheit erreicht. insert kann nur Argumente vom Typ Zeiger-auf-Foo oder Objekt-abgeleitet-von-Foo haben. Dadurch speichert der zugrundeliegende Container nur Zeiger, die auf Objekte vom Typ Foo zeigen. Das bedeutet, daß die Typumwandlung (cast) in FooList::peek() immer sicher ist und Sie eine korrekte FooList erzeugt haben. Um dies nun auch für BarList, BazList usw. zu erreichen, benötigen Sie wiederholte getrennte Klassendefinitionen. Um das Problem der wiederholten Klassendefinitionen zur Gewährleistung der Typsicherheit zu lösen, können Sie wiederum ein Templates verwenden. Siehe das Beispiel für typsichere generische Listenklassen.

Durch die Verwendung von Templates können Sie beliebige typsichere Listen mit einer einfachen Deklaration erstellen. Dabei wird durch die Typkonvertierungen von der umschließenden Klasse kein Programmcode erzeugt, so daß während der Laufzeit aufgrund dieser Typsicherheit kein zusätzlicher Aufwand entsteht.


Definition typsicherer generischer Listenklassen

template <class T> class List : public GList

{

public:

   void insert( T *t ) { GList::insert( t ); }

   T *peek() { return (T *)GList::peek(); }

    .

    .

    .

};

  // Erzeugen eines Listenobjektes vom Typ Foo und bennenen mit fList.

  List<Foo> fList;

  // Erzeugen eines Listenobjektes vom Typ Bar und bennenen mit bList.

  List<Bar> bList;

  // Erzeugen eines Listenobjektes vom Typ Baz und bennenen mit zList.

List<Baz> zList;


Eliminieren von Zeigern in Templates

Templates

Eine weitere Design-Technik ist das Einbinden von aktuellen Objekten, wodurch Zeiger unnötig werden. Da hier der Compiler die tatsächlichen Typen der Objekte kennt, kann so die Zahl der erforderlichen Aufrufe virtueller Funktionen reduziert werden. Dies ist ein großer Vorteil, wenn die virtuellen Funktionen klein genug sind, um sie als aInline-Funktionen verwenden zu können. Es ist aber schwierig, Inline-Funktionen zu verwenden, wenn sie über Zeiger aufgerufen werden, da in diesem Fall der Compiler die tatsächlichen Typen der Objekte, auf die gezeigt wird, nicht kennt.

template <class T> aBase {

    .

    .

    .

 private:

  T buffer;

};

class anObject : public aSubject, public aBase<aFilebuf> {

    .

    .

    .

};

Alle Funktionen in aBase können in aFilebuf definierte Funktionen direkt aufrufen, ohne dafür einen Zeiger zu benötigen. Wenn eine der in aFilebuf vorhandenen Funktionen inline verwendet werden kann, erreichen Sie eine höhere Ausführungsgeschwindigkeit, da die Templates das erlauben.


Verwenden von Compiler-Optionen für Templates

Templates

Die Optionsfamilie -Jg steuert die Art und Weise, in der vom Compiler Template-Instanzen erzeugt werden. Jede Template-Instanz, auf die der Compiler trifft, ist vom Wert der Option an dem Punkt betroffen, an dem die betreffende Instanz zum ersten Mal vom Compiler registriert wird.

Bei Template-Funktionen bezieht sich die Option auf die Instanzen der Funktionen, bei Template-Klassen auf alle Elementfunktionen und statischen Datenelemente der Template-Klasse. Grundsätzlich beeinflußt die Option nur die vom Compiler erzeugten und nicht die vom Anwender definierten Template-Instanzen. Mit Hilfe der Option kann dem Compiler allerdings mitgeteilt werden, welche Instanzen benutzerdefiniert sein werden, so daß sie nicht durch das Template erzeugt werden.


Verwenden von Compiler-Optionen für Templates

Bei der Erzeugung von Template-Instanzen mit Hilfe der Optionsfamilie -Jg gibt es zwei mögliche Vorgehensweisen:

Vorgehensweise 1

Nehmen Sie den Funktionsrumpf (bei einem Funktions-Template) oder die Definitionen der Elementfunktion und der statischen Datenelemente (bei einer Template-Klasse) in die Header-Datei auf, in der das betreffende Template definiert ist, und verwenden Sie die Standardeinstellung der Template-Option (-Jg). Wenn einige Instanzen des Templates benutzerdefiniert sind, sollten dessen Deklarationen (z.B. die Prototypen) in dieselbe Header-Datei aufgenommen werden. Ihnen muß dann allerdings #pragma option -Jgx vorangestellt werden

Siehe das Beispiel für Header-Dateien von Templates.

Vorgehensweise 2

Compilieren Sie alle Quellcodedateien Ihres Programms mit der Option -Jgx  ( wodurch externe Referenzen auf Templates erzeugt werden). Damit die Definitionen für alle Template-Instanzen zur Verfügung stehen, fügen Sie eine Datei (oder mehrere Dateien) in das Programm ein, das die Template-Rümpfe (einschließlich benutzerdefinierter Instanzen-Definitionen) enthält. Führen Sie alle Template-Instanzen auf, die für den Rest des Programms erforderlich sind, um damit die notwendigen Symboldefinitionen verfügbar zu machen. Compilieren Sie die Datei (oder die Dateien) mit der Option -Jgd. Siehe das Beispiel für die Compilierung von Templates in getrennten Dateien..


Template Header-Datei

// Deklarieren einer Template-Funktion und erzeugen seines Rumpfes.

/* Wenn diese Header-Datei in eine Quelltextdatei von C++ eingebunden wird, kann das Template sort verwendet werden, ohne daß man sich drüber Gedanken machen muß, wie die verschiedenen Instanzen erzeugt werden (mit der Ausnahme von sort bei Arrays vom Typ integer in einer vom Anwender definierten Instanz. Diese Definition muß vom Anwender bereitgestellt werden.) */

template<class T> void sort (T* array, int size)

{

   // Rumpf des Templates kommt hierhin.

}

// Das Sortieren von Elementen des Typs integer wir von einer

 vom Anwender definierten Instanz ausgeführt.

#pragma option -Jgx

extern void sort(int *array, int size);

// Stellt den ursprünglichen Status des Template-Schalters  wieder her.

#pragma option -Jg


Compilierung von Templates in getrennten Dateien

// In vector.h

template <class elem, int size> class vector

{

   elem * value;

public:

   vector();

   elem & operator [ ] (int index) {

      return value[index];

      }

};

// In der Quelltextdatei main.cpp.

#include "vector.h"

/** Läßt den Compiler wissen, daß die folgenden Template-Instanzen woanders definiert sind. **/

#pragma option -Jgx

// Verwendet zwei Instanzen der Template-Klasse vektor.

vector<int, 100> int_100;

vector<char, 10> char_10;

int main( )

{

   return int_100[ 0 ] + char_10[ 0 ];

}

// In der Quelltextdatei template.cpp.

#include <string.h>

#include "vector.h"

// Definieren beliebiger Rümpfe von Templates.

template <class elem, int size> vector <elem, size> :: vector()

{

   value = new elem[size];

   memset(value, 0, size * sizeof(elem) );

}

// Generieren der nötigen Instanzen.

#pragma option -Jgd

typedef vector<int, 100> fake_int_100;

typedef vector<char, 10> fake_char_10;


Exportieren und importieren von Templates

Siehe auch

Die Deklaration einer Template-Funktion oder eines Klassen-Templates benötigt ausreichend Flexibilität, um die Verwendung in einer DLL oder einer EXE-Datei zu ermöglichen. Die gleiche Templatedeklaration einer Templatefunktion sollte importierbar und/oder exportierbar verfügbar sein, oder ohne einen Modifizierer. Um völlig flexibel zu sein, sollten die Deklarationen in der Header-Datei des Templates keine Modifizierer _ _export oder _ _import verwenden

Die folgenden Schritte demonstrieren das exportieren und importieren von Templates. Der Quelltext ist in drei Dateien organisiert. Wenn die Header-Datei verwendet wird, ist der Quelltext in der DLL generiert. Eine DLL-Bibliothek wird erzeugt und zu einer EXE-Datei gebunden.

1.	Exportierbare/importabierbare Deklarationen von Templates

Die Header-Datei enthält die Deklarationen aller Klassen-Templates und Template-Funktionen. Eine Version der Templates zum exportieren/importieren kann instantiiert werden, indem das passende Makro zur Compilierzeit definiert wird.

2.	Compilieren von exportierbaren Templates

Schreiben Sie den Quelltext für eine DLL. Wenn sie compilert ist, verfügt diese DLL über wiederverwendbaren Quelltext zum exportieren von Templates.

3.	Verwenden von ImportTemplates

Nun können Sie eine aufrufende Funktion schreiben, die Templates verwendet. Diese Datei ist zu der DLL gebunden. Nur Objekte, die nicht in der Header-Datei gebunden sind und die in der Funktion main instantiiert sind, sind für den Compiler ein Grund, neuen Quelltext zu generieren. Der Quelltext für ein neu instantiiertes Objekt ist in der Daten MAIN.OBJ beschrieben.


Präprozessor-Direktiven

Präprozessor-Direktiven werden gewöhnlich am Anfang des Quellprogramms plaziert, sind aber an jeder beliebigen Stelle des Programms zulässig. Der Präprozessor von Borland C++ erkennt Präprozessor-Direktiven (auch Steuerzeilen genannt) und zerlegt sie in die darin enthaltenen Token. Borland C++ kennt folgende Präprozessor-Direktiven:

# (Null-Direktive)	#ifdef

#define	#ifndef

#elif	#include

#else	#line

#endif	#pragma

#error	#undef

#if

Jede Zeile, die mit dem Zeichen # beginnt, wird als Präprozessor-Direktive betrachtet, falls das Zeichen # nicht Teil einer String-Konstanten, einer Zeichenkonstanten oder eines Kommentars ist. Vor und nach dem einleitenden #-Zeichen dürfen Whitespace-Zeichen (außer Zeilenvorschub) stehen.


# (Null-Direktive)

Direktiven

Syntax

#

Beschreibung

Die Null-Direktive besteht aus einer Zeile, die nur das Zeichen # enthält. Diese Zeile wird stets ignoriert.


#define

Siehe auch	Beispiel	Direktiven

#define makro_name <token_folge>

Beschreibung

Die Direktive #define definiert ein Makro. Makros stellen einen Mechanismus zur Verfügung, mit dem Token ersetzt werden können, wobei dies mit oder ohne einen Satz von formalen Parametern (ähnlich wie ein Funktionsaufruf) geschehen kann.

Jedes Mal, wenn makro_name in Ihrem Quellprogramm nach dieser Steuerzeile auftritt, wird er durch die möglicherweise leere token_folge ersetzt und zwar an seiner Originalposition (es gibt dabei einige Ausnahmen, die später behandelt werden). Diese Ersetzung nennt man Makroerweiterung. Die token_folge wird manchmal Rumpf des Makros genannt.

Eine leere Token-Folge bewirkt, daß der betreffende Makrobezeichner an jeder Stelle aus dem Quellprogramm entfernt wird.

Nach jeder einzelnen Makroerweiterung wird der dadurch neu erzeugte Text nochmals durchsucht. Damit wird eine Schachtelung von Makros möglich: Der erweiterte Text kann neue Makronamen enthalten, die auch wieder ersetzt werden. Wenn ein Makro jedoch in einen Text umgewandelt wird, der wie eine Präprozessor-Direktive aussieht, wird diese nicht vom Präprozessor erkannt. Für die Makroerweiterung gibt es folgende Einschränkungen:

Tritt ein Makrobezeichner im Quelltext innerhalb von String-Konstanten, Zeichenkonstanten oder Kommentaren auf, so wird keine Erweiterung durchgeführt.

Ein Makro wird nicht während seiner eigenen Erweiterung erweitert (#define A A wird also nicht unendlich oft erweitert).

Beispiele

#define HI "Ich wünsche Ihnen einen schönen Tag!"

#define empty

#define NIL ""

#define GETSTD #include <stdio.h>


#undef

Siehe auch	Beispiel	Direktiven

Syntax

#undef macro_identifier

Beschreibung

Mit der Direktive #undef können Sie ein Makro außer Kraft setzen. Dabei wird eine vorher bestehende Verbindung zwischen Makrobezeichner und Token-Folge aufgelöst. Die Makrodefinition wird praktisch "vergessen" und der Makrobezeichner ist dann undefiniert. Innerhalb einer #undef-Zeile findet keine Makroerweiterung statt.

Der Status "definiert" bzw. "undefiniert" stellt eine wichtige Eigenschaft eines Bezeichners dar, unabhängig von seiner tatsächlichen Definition. Die Bedingungs-Direktiven #ifdef und #ifndef werden benutzt, um zu prüfen, ob ein Bezeichner momentan definiert ist oder nicht und bieten damit einen flexiblen Mechanismus zur Steuerung vieler Aspekte der Compilierung.

Nachdem die Definition eines Makrobezeichners aufgehoben worden ist, kann er mit #define neu definiert werden, wobei man die frühere oder eine andere Token-Folge verwenden kann.

Der Versuch, einen bereits definierten Makrobezeichner erneut zu definieren, führt zu einer Warnung, falls die neue Definition nicht exakt Token für Token der existierenden entspricht. Wenn Definitionen in anderen Header-Dateien vorkommen können, ist folgende Strategie empfehlenswert:

#ifndef BLOCK_SIZE

  #define BLOCK_SIZE 512

#endif

Die mittlere Zeile wird ignoriert, wenn BLOCK_SIZE zur Zeit definiert ist. Ist BLOCK_SIZE dagegen zur Zeit nicht definiert, so erfolgt die Definition nunmehr in der mittleren Zeile.

Es ist kein Semikolon (;) erforderlich, um eine Präprozessor-Direktive abzuschließen. Jedes Zeichen in der Token-Folge, einschließlich der Semikolons, taucht in der Makroerweiterung auf. Die Token-Folge endet mit dem ersten Zeilenvorschubzeichen, dem kein Backslash vorangeht. Jede Folge von Whitespace-Zeichen (einschließlich Kommentaren) in der Token-Folge wird durch ein einziges Leerzeichen ersetzt.


Beispiele

#define BLOCK_SIZE 512

  .

  .

  .

#undef BLOCK_SIZE

/* Das Verwenden von BLOCK_SIZE ist ab hier unzulässig ("Unbekannter" Bezeichner) */

  .

  .

  .

#define BLOCK_SIZE 128   /* Neudefinition mit anderem Wert */


Verwendung der Befehlszeilenoptionen -D und -U.
Verwenden der Kommandozeilenoptionen -D und -U

Siehe auch

Mit Hilfe der Kommandozeilenoptionen -D und -U. des Compilers können Bezeichner definiert oder ihre Definitionen gelöscht werden.

Die Kommandozeile

BCC32 -Ddebug=1; paradox=0; X -Umysym myprog.c

ist äquivalent zu dem Programmstück

#define debug 1

#define paradox 0

#define X

#undef mysym


Schlüsselwörter und reservierte Wörter in Makros
Schlüsselwörter und reservierte Wörter als Makros

Es ist zulässig, aber nicht empfehlenswert, Schlüsselwörter von Borland C++ als Makronamen zu verwenden:

#define int long    /* zulässig, aber wahrscheinlich katastrophal */

#define INT long    /* zulässig und möglicherweise nützlich */

Die folgenden vordefinierten globalen Namen dürfen nicht in einer #define oder #undef-Direktive verwendet werden:

_ _DATE_ _	_ _FILE_ _	_ _LINE_ _

_ _STDC_ _	_ _TIME_ _


Makros mit Parametern

Siehe auch

Für die Definition eines Makros mit Parametern gilt die folgende Syntax:

#define macro_identifier(<argument_liste>) token_sequence

Ein Komma innerhalb von runden Klammern in der Argumentliste wird als Teil des Arguments und nicht als Begrenzter des Arguments betrachtet.

Beachten Sie, daß kein Leerzeichen zwischen dem Makrobezeichner und der runden Klammer ( zulässig ist. Die optionale argument_liste ist eine durch Kommata getrennte Aufzählung von Bezeichnern, ähnlich der Argumentliste einer C-Funktion. Jeder durch Kommata getrennten Bezeichner stellt ein formales Argument (Platzhalter) dar.

Ein solches Makro ruft man in nachfolgenden Programmteilen auf durch

makro_identifier <whitespace>(<liste_der_aktuellen_argumente>)

Die Syntax ist identisch mit der eines Funktionsaufrufs, tatsächlich sind auch viele Funktionen der C-Standardbibliothek in Wirklichkeit als Makros realisiert. Es gibt jedoch einige wichtige semantische Unterschiede, Seiteneffekte und mögliche "Fallgruben".

Die optionale liste_der_aktuellen_argumente muß exakt dieselbe Anzahl von durch Kommata getrennten Token-Folgen (aktuelle Argumente genannt) enthalten wie die formale argument_liste in der #define-Zeile: Für jedes formale Argument muß ein aktuelles angegeben werden. Falls die Zahl der Argumente in beiden Listen unterschiedlich ist, wird eine Fehlermeldung ausgegeben.

Ein Makroaufruf bewirkt zwei Ersetzungsvorgänge. Zuerst werden der Makrobezeichner und die in Klammern eingeschlossenen Argumente durch die Token-Folge ersetzt. Danach werden alle in der Token-Folge auftretenden formalen Argumente durch die jeweiligen aktuellen Argumente aus liste_der aktuellen_argumente ersetzt.

Wie bei einfachen Makrodefinitionen erfolgt nun ein erneutes Durchsuchen, um etwaige eingebettete Makrobezeichner zu finden und zu erweitern.


Verschachteln von Klammern und Kommas
Verschachteln von Klammern und Kommata

Die liste_der_aktuellen_argumente darf verschachtelte runde Klammern enthalten, sofern sie korrekt paarig sind. Außerdem dürfen Kommata innerhalb von Anführungszeichen oder runden Klammern stehen, sie werden dann nicht als Argument-Trennzeichen interpretiert.

#define ERRMSG(x, str) showerr("Error:, x. str)

#define SUM(x,y)  ((x) + (y))

.

.

.

ERRMSG(2, "Press Enter, then Esc");

/* expands to:  showerr("Error",2,"Press Enter, then Esc");

return SUM(f(i, j), g(k,l));

/* expands to: return ((f(i,j) + (g(k,l))); */


Token verschmelzen mit ##

Sie können zwei Token verschmelzen, indem Sie die Zeichen ## (plus optionalem Whitespace auf beiden Seiten) einfügen. Der Präprozessor entfernt Whitespace und ## und verbindet die beiden einzelnen Token zu einem neuen. Auf diese Weise können Sie zum Beispiel Bezeichner "konstruieren".

Bei der Definition

#define VAR(i, j) (i##j)

 expandiert der Aufruf VAR(x, 6) zu(x6). Dies ersetzt die alter, nichtportable Methode der Verwendung von (i/**/j).


Umwandlung in Strings mit #
Umwandeln in Strings mit #

Das Zeichen # kann vor ein formales Makroargument gesetzt werden, um das aktuelle Argument nach dem Ersetzen in einen String umzuwandeln.

Bei der Definition:

#define TRACE(flag) printf(#flag "=%d\n", flag)

wird der Quelltextauschnitt

int highval = 1024;

TRACE(highval);

zu

int highval = 1024;

printf("highval" "=%d\n", highval);

welches behandelt wird als

int highval = 1024;

printf("highval=%d\n", highval);


Verwenden des Backslash (\) zum Aufbrechen von Zeilen
Verwenden des Backslash (\) für Fortsetzungszeilen

Eine lange Token-Folge kann mit Hilfe des Backslash-Zeichens (\) über eine Zeile hinaus ausgedehnt werden. Der Backslash und das nachfolgende Zeilenvorschub-Zeichen werden entfernt, um die für die Erweiterung erforderliche "echte" Token-Folge zu erhalten.

#define WARN "This is really a single-\

line warning."

.

.

.

puts(WARN)

/* Bildschirm wird anzeigen: This is really a single-line warning. */


Seiteneffekte und andere Gefahren

Die Ähnlichkeiten zwischen Funktions- und Makroaufrufen verdecken oft deren Unterschiede. Bei einem Makroaufruf gibt es keine eingebaute automatische Typprüfung, so daß eine Nichtübereinstimmung der Datentypen von formalen und aktuellen Argumenten ohne direkte Fehlermeldung äußerst merkwürdige, schwer nachzuvollziehende Ergebnisse bewirken kann. Makroaufrufe können auch zu unerwünschten Seiteneffekten führen, insbesondere dann, wenn ein aktuelles Argument mehrmals ausgewertet wird.

Vergleichen Sie CUBE und cube im folgenden Beispiel.

int cube(int x) {

   return x* x*x;

   }

#define CUBE(x)  ( (x)* (x) * (x) )

...

int b = 0, a = 3;

b = cube(a++);

/* cube() is passed actual arg = 3; so b = 27; a now = 4 */

a = 3;

b = CUBE(a++);

/* expands as ((a++)*(a++)*(a++)); a now = 6 */


#include

Beispiel	Direktiven

Syntax

#include <header_name>

#include "header_name"

#include makro_name

Beschreibung

Die Direktive #include fügt andere, benannte Dateien (Include-Dateien, Header-Dateien oder Header genannt) in das Quellprogramm ein. Die Syntax kennt drei Formen:

Die erste und die zweite Form setzen voraus, daß keine Makroerweiterung stattfinden soll; anders gesagt, header_name wird nicht nach Makronamen durchsucht. header_name muß einen zulässigen Dateinamen mit Erweiterung (traditionell ist dies .h für Header) und optional mit einer Pfadangabe (und Pfadbegrenzer-Zeichen) darstellen.

Die dritte Form setzt voraus, daß nach #include weder ein < noch ein " als erstes Nicht-Whitespace-Zeichen steht. Außerdem wird vorausgesetzt, daß eine Makrodefinition existiert, die den Makronamen zu einem gültigen, begrenzten Header-Dateinamen im Format <header_name> oder "header_name" erweitert.

Der Präprozessor entfernt die #include-Zeile und ersetzt sie wirkungsmäßig an ihrer Position im Quelltext durch den gesamten Quelltext der Header-Datei. Die Quelltextdatei selbst wird nicht wirklich verändert, aber der Compiler "sieht" den ergänzten Text. Das Einfügen einer #include-Direktive kann also Gültigkeitsbereich und -dauer von Bezeichnern der eingefügten Datei beeinflussen.

Wenn Sie in header_name explizit einen Pfadnamen angeben, so wird nur das angegebene Verzeichnis durchsucht.

Der Unterschied zwischen den Formen <header_name> und "header_name" liegt in der Suchstrategie für das Auffinden der Include-Datei.


Suche nach einer Header-Datei mit <Header-Dateiname>
Header-Datei: Suche danach bei der Form <header_name>

Die Version <header_name> gibt eine Standard-Includedatei an; die Suche erfolgt in jedem der Include-Verzeichnisse, in der Folge wie sie angegeben wurden. Falls sich die Datei in keinem der Vorgabeverzeichnisse befindet, erscheint eine Fehlermeldung.


Suche nach einer Header-Datei mit "Header-Dateiname"
Header-Datei: Suche danach bei der Form "header_name"

Die Form header_name spezifiziert eine vom Programmierer bereitgestellte Include-Datei. Sie wird zuerst im aktuellen Verzeichnis (normalerweise das Verzeichnis mit den gerade compilierten Dateien) gesucht. Wird die Datei dort nicht gefunden, so wird die Suche in den Include-Verzeichnissen wie bei der Form <header_name> fortgesetzt.


Beispiele

Die folgende #include-Direktive bewirkt, daß der Präprozessor nach stdio.h im voreingestellten Include-Verzeichnis sucht.

#include <stdio.h>

Die folgende #include-Direktive bewirkt, daß der Präprozessor nach MEININCL.H erst im aktuellen Verzeichnis und danach in den voreingestellten Verzeichnissen sucht.

#include "meinincl.h"

Nach der Makroerweiterung bewirkt die folgende #include-Direktive, daß der Präprozessor  nach C:\BCB\INCLUDE\MEINS.H und nichts anderem sucht.

#define myinclud "C:\BCB\INCLUDE\MEINS.H"

/* Hinweis: Einfache Backslashs sind hier zulässig, in einer C-Anweisung

   müßten Sie aber "C:\\BCB\\INCLUDE\\MEINS.H" schreiben. */

#include  myinclud

/* Makroerweiterung */


Bedingte Compilierung

C++Builder unterstützt die bedingte Compilierung, indem er die entsprechenden Quelltextzeilen mit Leerzeilen ersetzt. Die auf diese Art ignorierten Zeilen, sowie alle Zeilen, die über entsprechende Direktiven nicht compiliert werden sollen, fangen mit # an (außer bei den Direktiven #if, #ifdef, #ifndef, #else, #elif, und #endif). Alle Direktiven für die bedingte Compilierung müssen in den Quelltext- oder Include-Dateien abgeschlossen werden, in denen sie begonnen wurden.


defined

Siehe auch	Schlüsselwörter

Syntax

#if defined[(] <bezeichner> [)]

#elif defined[(] <bezeichner> [)]

Beschreibung

Der Operator defined wird benutzt, um zu prüfen, ob ein Bezeichner zuvor mit der Direktive #define definiert worden ist. Der Operator defined darf nur in #if- und #elif- Konstrukten verwendet werden.

Wenn <bezeichner> zuvor definiert und diese Definition nicht zwischenzeitlich per #undef wieder gelöscht wurde, so ergibt defined den Wert 1 (True), andernfalls 0 (False).

defined hat dieselbe Funktion wie #ifdef.

#if defined(mein_symbol)

ist gleichwertig mit

#ifdef meinsymbol

Der Vorteil von defined liegt darin, daß man dieses Schlüsselwort innerhalb eines komplexen Ausdrucks hinter der Direktive #if mehrfach einsetzen kann. Beispiel dafür:

#if defined(mein_symbol) && !defined(dein_symbol)


#if, #elif, #else, und #endif
#if, #elif, #else, and #endif

Siehe auch	Direktiven

Syntax

#if constant-expression-1

<section-1>

<#elif constant-expression-2 newline section-2>

  .

  .

  .

<#elif constant-expression-n newline section-n>

<#else <newline> final-section>

#endif

Beschreibung

Borland C++ unterstützt die bedingte Compilierung durch Ersetzen der entsprechenden Quelltextzeilen durch Leerzeilen. Es werden alle Zeilen ignoriert, die gemäß Präprozessor-Direktiven nicht compiliert werden sollen. Alle Direktiven für bedingte Compilierung müssen in derjenigen Quelltext- oder Include-Datei abgeschlossen werden, in der sie beginnen.

Die Direktiven zur bedingten Compilierung, #if, #elif, #else und #endif, wirken ähnlich wie die normalen Bedingungsoperatoren von C. Wenn  konstantenausdruck_1 (Gegenstand einer Makroerweiterung) einen Wert ungleich Null (true) ergibt, werden die (möglicherweise leeren) Quelltextzeilen in abschnitt_1 (ganz gleich, ob Präprozessor-Direktiven oder normaler Quelltext) vom Präprozessor verarbeitet und dann gegebenenfalls dem Borland C++ Compiler übergeben. Andernfalls, wenn also konstantenausdruck_1 den Wert null ergibt, wird abschnitt_1 ignoriert (keine Makroerweiterung und keine Compilierung).

Im Fall, daß die Bedingung true ist, wird, nachdem abschnitt_1 vom Präprozessor verarbeitet wurde, der Quelltext bis zur zugehörigen #endif-Direktive (die diesen Block der bedingten Compilierung beendet) übergangen und mit nächster_abschnitt fortgefahren. Ist die Bedingung false, so geht die Kontrolle an die nächste #elif-Zeile (falls vorhanden) über, in der konstantenausdruck_2 berechnet wird. Ergibt sich dabei der Wert true, so wird abschnitt_2 verarbeitet, wonach die Kontrolle zur zugehörigen #endif-Direktive geht. Andernfalls, wenn also konstantenausdruck_2 den Wert false hat, geht die Kontrolle zur nächsten #elif-Direktive, und so geht es weiter, bis entweder #else oder #endif erreicht wird. Die optionale #else-Direktive dient als Alternativzweig, falls alle vorangegangenen Prüfungen den Wert false ergeben haben. Die Direktive #endif schließt die Gruppe der bedingten Anweisungen ab.

Der verarbeitete Abschnitt kann selbst weitere Bedingungs-Direktiven enthalten, die beliebig tief verschachtelt sein können; zu jeder #if-Direktive muß aber genau eine zugehörige #endif-Direktive vorhanden sein.

Das Endergebnis des vorstehenden Beispiels ist, daß nur ein einziger (möglicherweise leerer) Abschnitt weiterverarbeitet wird. Die übergangenen Abschnitte sind nur relevant, um die Struktur verschachtelter Bedingungen nicht zu verletzen und es zu ermöglichen, daß jedem #if das richtige #endif zugeordnet werden kann.

Der Konstantenausdruck, dessen Wert geprüft wird, muß einen konstanten, ganzzahligen Wert liefern.


#ifdef und #ifndef

Siehe auch	Direktiven

Syntax

#ifdef identifier

#ifndef identifier

Beschreibung

Mit den Bedingungs-Direktiven #ifdef und #ifndef können Sie prüfen, ob ein Bezeichner momentan definiert ist oder nicht, ob also eine #define-Direktive für diesen Bezeichner vorausging und noch wirksam ist. Die Zeile

#ifdef identifier

hat, wenn identifier momentan definiert ist, exakt denselben Effekt wie

#if 1

und wenn identifier momentan nicht definiert ist, denselben Effekt wie

#if 0

Die Direktive #ifndef ergibt true, wenn identifier momentan nicht definiert ist; deshalb hat die Zeile

#ifndef identifier

exakt denselben Effekt wie

#if 0

wenn identifier momentan definiert ist, und denselben Effekt wie

#if 1

wenn identifier momentan nicht definiert ist.

Die Syntax entspricht demnach der der Direktiven #if, #elif, #else und #endif.

Ein Bezeichner, der als NULL definiert wurde, gilt als definiert.


#line

Direktiven

Syntax

#line integer_constant  <"dateiname">

Beschreibung

Sie können die #line-Direktive verwenden, um einem Programm Zeilennummern für Querverweise oder Fehlermeldungen zu übergeben. Wenn Ihr Programm aus irgendwelchen anderen Programmen entnommene Passagen enthält, ist es oft sinnvoll, diese Passagen mit den Zeilennummern des ursprünglichen Quellprogramms zu versehen, anstatt das zusammengesetzte Programm komplett sequentiell durchzunummerieren.

Die Direktive #line legt fest, daß die folgende Quelltextzeile ursprünglich die Zeile mit der Nummer integer_konstante der Datei dateiname war. Nachdem der Dateiname einmal registriert ist, kann man bei nachfolgenden #line-Direktiven, die sich auf dieselbe Datei beziehen, auf die explizite Angabe des Arguments dateiname verzichten.

Makros werden in #line-Argumenten genauso wie in der Direktive #include erweitert.

Die Direktive #line wird hauptsächlich von Hilfsprogrammen, die C-Quelltexte erzeugen, benutzt, weniger von "menschlichen" Programmierern.


#error

Beispiel	Direktiven

Syntax

#error errmsg

Beschreibung

Die Präprozessor-Direktive #error erzeugt die Botschaft:

Error: Dateiname Zeilennummer# : Error-Direktive: errmsg

Diese Direktive wird gewöhnlich in eine bedingte Präprozessor-Direktive eingefügt, die eine bestimmte unerwünschte Bedingung zur Compilierzeit abfangen soll. Im Normalfall hat diese Bedingung den Wert false. Ist die Bedingung aber true, soll der Compiler eine Fehlermeldung ausgeben und die Übersetzung stoppen. Um dies zu erreichen, setzt man die Direktive #error zusammen mit einer Bedingungs-Direktive ein, die im unerwünschten Fall true ergibt.


Beispiel

#if (MYVAL != 0 && MYVAL != 1)

#error MYVAL muß entweder 0 oder 1 sein

#endif


Übersicht über #pragma

Direktiven

Syntax

#pragma directive_name

Beschreibung

Mit Hilfe der Direktive  #pragma kann der Borland C++ Compiler beliebige gewünschte Anweisungen definieren, ohne dabei mit anderen Compilern in Konflikt zu kommen, die ebenfalls mit #pragma arbeiten. Wenn ein Compiler directive_name nicht kennt, so ignoriert er die gesamte #pragma-Direktive ohne Fehler- oder Warnmeldung.

Borland C++ unterstützt folgende #pragma-Direktiven:

#pragma anon_struct

#pragma argsused

#pragma codeseg

#pragma comment

#pragma exit

#pragma hdrfile

#pragma hdrstop

#pragma inline

#pragma intrinsic

#pragma link

#pragma message

#pragma option

#pragma resource

#pragma startup

#pragma warn


#pragma anon_struct

Siehe auch	#pragma

Syntax

#pragma anon_struct on

#pragma anon_struct off

Beschreibung

Mit der Direktive anon_struct können Sie anonyme Strukturen kompilieren, die in Klassen eingebettet sind.

#pragma anon_struct on

struct S {

   int i;

   struct {  // Eingebettete anonyme Struktur

      int   j ;

      float x ;

   };

   class {  // Eingebettete anonyme Struktur

   public:

      long double ld;

   };

S() { i = 1; j = 2; x = 3.3; ld = 12345.5;}

};

#pragma anon_struct off

void main() {

   S mystruct;

   mystruct.x = 1.2;  // Eingebetteten Daten zuweisen

   }


#pragma argsused

Siehe auch	#pragma

Syntax

#pragma argused

Beschreibung

Das Pragma argsused ist nur zwischen Funktionsdefinitionen zulässig; es wirkt nur auf die nächstfolgende Funktion. Das Pragma unterbindet die Warnbotschaft:

"Parameter name is never used in function func-name"


#pragma codeseg

Siehe auch	#pragma

Syntax

#pragma codeseg <seg_name> <"seg_class"> <group>

Beschreibung

Mit der Direktive codeseg können Sie den Segment-, Klassen- oder Gruppennamen für Funktionen angeben. Wenn das Pragma ohne Argumente verwendet wird, so wird das Standard-Code-Segment für Funktionen benutzt.


#pragma comment

Siehe auch	#pragma

Syntax

#pragma comment (comment type, "string")

Beschreibung

Mit dem Pragma comment können Sie Kommentar-Records in eine OBJ-Datei einfügen. Der Parameter comment type kann dabei einen der folgenden Werte annehmen:

Wert	Erklärung

exestr	Der Linker schreibt string in eine .OBJ-Datei, die wiederum in das ausführbare Programm eingebunden wird. Dieser so plazierte String wird zwar niemals in den Speicher geladen, kann aber durch geeignete Hilfsprogramme (zum Durchsuchen) in der ausführbaren Programmdatei gefunden werden.

lib	Schreibt einen Kommentar-Record in eine OBJ-Datei. Der Kommentar-Record wird vom Linker für das Durchsuchen der Bibliotheken herangezogen. Ein nicht in der Response-Datei des Linkers spezifiziertes Bibliothekenmodul kann durch eine comment LIB-Direktive angegeben werden. Dadurch wird der Linker veranlaßt, das Modul lib_module_name (in string spezifiziert) als letzte Bibliothek einzubinden. Mehrere Module können benannt und in der Reihenfolge ihrer Nennung eingebunden werden.

user	Der Compiler schreibt string in eine OBJ-Datei. Der angegebene String wird vom Linker ignoriert.


#pragma exit und #pragma startup

Siehe auch	#pragma

Syntax

#pragma startup function_name <priorität>

#pragma exit function_name <priorität>

Beschreibung

Mit diesen beiden Pragmas kann man vom Programm aus Funktionen spezifizieren, die beim Programmstart (vor dem Aufruf von main) beziehungsweise beim Programmende (unmittelbar, bevor die Funktion _exit ausgeführt wird) aufgerufen werden sollen.

Die angegebenen Funktionen müssen vorher deklariert worden sein, dürfen keine Argumente benötigen und müssen ein Ergebnis vom Typ void zurückliefern. Anders gesagt, sie müssen in der Form

void funktion(void);

deklariert werden. Der optionale Parameter priorität sollte ein ganzzahliger Wert von 64 bis 255 sein. Der Wert 0 bezeichnet die höchste Priorität. Die Funktionen mit der höchsten Priorität werden beim Programmstart als erste und beim Programmende als letzte aufgerufen. Wenn Sie keine Priorität vorgeben, gilt die Voreinstellung 100.

Hinweis:	Die Prioritätswerte von 0 bis 63 werden von den C-Bibliotheken benutzt und sollten deshalb vom Anwendungsprogrammierer nicht verwendet werden.


#pragma hdrfile

Siehe auch	#pragma

Syntax

#pragma hdrfile "filename.CSM"

Beschreibung

Diese Direktive bestimmt den Namen der Datei, in der die vorcompilierten Header gespeichert werden. Weitere Informationen können Sie dem  Anhang C des Benutzerhandbuchs entnehmen.

Wenn Sie keine vorcompilierten Header verwenden, hat diese Direktive keine Wirkung. Mit Hilfe der Kommandozeilenoption H=filename oder der Menüeinstellung Vorcompilierte Header (O/C/Codegenerierung) können Sie den Namen der Datei mit den vorcompilierten Headern ändern.


#pragma hdrstop

Siehe auch	#pragma

Syntax

#pragma hdrstop

Beschreibung

Diese Direktive schließt die Liste der Header-Dateien ab, die für die Vorcompilierung vorgesehen sind. Damit können Sie den für die vorcompilierten Header benötigten Plattenspeicherplatz reduzieren. Weitere Informationen zu vorcompilierten Headern finden Sie im Anhang C des Benutzerhandbuchs.


#pragma inline

Siehe auch	#pragma

Syntax

#pragma inline

Beschreibung

Diese Direktive ist äquivalent zur Kommandozeilenoption -B des Compilers oder der inline-Option der IDE. Sie teilt dem Compiler mit, daß der Quelltext Teile in Assemblersprache enthält.

Diese Zeile setzt man am besten an den Anfang des Quelltextes, weil der Compiler sich mit der Option -B selbst erneut startet, wenn er auf die Direktive #pragma inline stößt.


#pragma intrinsic

Siehe auch	Beispiel	#pragma

Syntax

#pragma intrinsic [-]function-name

Beschreibung

Verwenden Sie  #pragma intrinsic, um Kommandozeilenoptionen oder IDE-Optionen, die mit Inline-Funktionen zu tun haben, zu übergehen.

Wenn eine Intrinsinc-Funktion in Inline-Form benutzt werden soll, muß stets ein Prototyp für diese Funktion vor ihrer Verwendung ins Programm aufgenommen werden.


Beispiele

Das folgende Beispiel bewirkt, daß der Compiler den Code für die Funktion strcpy als Teil Ihrer Funktion generiert:

#pragma intrinsic strcpy

Durch folgende Direktive wird der Compiler an letzterem gehindert:

#pragma intrinsic -strcpy


#pragma link

Siehe auch	#pragma

Syntax

#pragma link „[pfad]modul-name[.dateierweiterung]“

Beschreibung

Diese Direktive wird automatisch von der DIE und dem DCC32-Compiler geschrieben. Die Direktive weist den Linker an, die Datei in eine ausführbare Datei einzubinden.

Der Linker sucht per Vorgabe nach modul-name im lokalen Verzeichnis und in jedem Pfad, der über die Option -L angegeben wurde. Sie können das Argument pfad verwenden, um ein Verzeichnis anzugeben.

Der Linker geht per Vorgabe von einer Dateierweiterung .obj aus.


#pragma message

Siehe auch	Beispiel	#pragma

Syntax

#pragma message ("text" ["text"["text" ...]])

#pragma message text

Beschreibung

Die Anweisung #pragma message dient dazu, vom Benutzer definierte Meldungen innerhalb des Programm-Codes zu spezifizieren.

Die erste (MSC-kompatible) Form verlangt, daß der Text aus mindestens einer String-Konstanten besteht und von runden Klammern eingeschlossen ist. Bei Verwendung der zweiten Form wird der auf die #pragma-Anweisung folgende Text für die Warnmeldung genommen. In jedem Fall werden Makro-Referenzen erweitert, bevor die Meldung angezeigt wird.

Benutzerdefinierte Meldungen werden standardmäßig angezeigt und können mit der IDE-Option Benutzerdefinierte Warnmeldungen ein- bzw. ausgeblendet werden. Diese Auswahlmöglichkeit entspricht dem Compiler-Schalter -wmsg.


Beispiel

Das folgende Beispiel zeigt entweder die Meldung "You are compiling using version xxx of C++Builder" (wobei xxx die Versionsnummer ist), oder die Meldung "Sorry, you are not using the C++Builder compiler" an.

#ifdef __BORLANDC__

#pragma message You are compiling using version __BORLANDC__ of C++Builder.

#else

#pragma message ("Sorry, you are not using the C++Builder compiler")

#endif


#pragma option

Siehe auch	#pragma

Syntax

#pragma option [optionen...]

Beschreibung

Benutzen Sie #pragma option, wenn Sie Kommandozeilenoptionen gleich in Ihren Quelltext aufnehmen wollen.

optionen kann jede Kommandozeilenoption sein (mit Ausnahme der im nächsten Absatz angegebenen). In einer Direktive kann eine beliebige Anzahl von Optionen kombiniert werden. Alle umschaltbaren Optionen (wie etwa -a oder -K) können wie in der Kommandozeile ein- und ausgeschaltet werden. Bei diesen umschaltbaren Optionen können Sie auch einen Punkt hinter die Option setzen, dadurch wird sie auf den durch die Kommandozeile, die Konfigurationsdatei oder das Optionen-Menü eingestellten Wert gesetzt. Dies ermöglicht es Ihnen, eine Option vorübergehend zu ändern und danach wieder die alte Einstellung zu aktivieren, ohne sich merken zu müssen (oder überhaupt nur wissen zu müssen), welche die alte Einstellung war.

Folgende Optionen dürfen nicht bei #pragma option verwendet werden:

-B	-c	-dname

-Dname=string	-efilename	-E

-Fx	-h	-lfilename

-lexset	-M	-o

-P	-Q	-S

-T	-Uname	-V

-X	-Y

Sie können #pragma-, #include-, #define- und einige #if-Direktiven in folgenden Fällen verwenden:

Vor der Verwendung irgendeines Makronamens, der mit zwei Unterstrichen beginnt (und deshalb möglicherweise für ein vordefiniertes Makro steht),  in einer #if-, #ifdef-, #ifndef- oder #elif -Direktive.

Vor dem Auftreten des ersten echten Tokens (also der ersten C- oder C++Deklaration).

Folgende Kommandozeilenoptionen können nur vor den eben erwähnten Ereignissen in der Direktive #pragma option stehen:

-Efilename	-f	-i#

-m*	-npath	-ofilename

-u	-W	-z

*

Andere Optionen können an jeder Stelle geändert werden. Die folgenden Optionen beeinflussen den Compiler nur dann, wenn sie zwischen Funktionen oder Objektdeklarationen geändert werden:

-1	-h	-r

-2	-k	-rd

-a	-N	-v

-ff	-O	-y

-G	-p	-Z

Die folgenden Optionen können jederzeit geändert werden und wirken sich sofort aus:

-A	-gn	-zE

-b	-jn	-zF

-C	-K	-zH

-d	-wxxx

Außerdem kann ihnen auch ein Punkt (.) nachgestellt werden, um sie auf ihre in der Kommandozeile vorgegebene Einstellung zurückzusetzen.


#pragma resource

Siehe auch	#pragma

Syntax

#pragma resource "*.dfm"

Beschreibung

Dieses pragma markiert die Datei als Formular-Unit und erfordert die passenden .dfm und Header-Dateien. Alle diese Dateien werden von der IDE verwaltet.

Wenn Ihr Formular Variablen benötigt, müssen diese gleich nach der Verwendung von pragma resource deklariert werden. Die Deklarationen müssen folgende Form haben:

   TFormName *Formname;


#pragma warn

Siehe auch	Beispiel	#pragma

Syntax

#pragma warn [+|-|.]www

Beschreibung

Das Pragma warn ermöglicht es Ihnen, bestimmte Kommandozeilenoptionen der Form -wxxx  zu übergehen oder die Einstellung Warnungen zu aktivieren.


Beispiele

Wenn Ihr Quelltext die folgenden Direktiven enthält

#pragma warn +xxx

#pragma warn -yyy

#pragma warn .zzz

so wird die Warnung xxx ein- und die Warnung yyy ausgeschaltet. Die Warnung zzz wird auf die Einstellung gesetzt, die zu Beginn der Compilierung der Datei gültig war.Eine vollständige Liste aller Warnungen und ihrer zugehörigen dreibuchstabigen Kürzel finden Sie unter Kommandzeilenoptionen.


Vordefinierte Makros

Siehe auch

Borland C++ definiert bestimmte globale Bezeichner vor, die als konstante Ausdrücke bekannt sind. Die meisten globalen Bezeichner beginnen und enden mit zwei Unterstrichen (__).

Hinweis:	Für eine bessere Lesbarkeit sind Unterstriche oft durch eine einzelnes Leerzeichen getrennt. In Ihrem Quelltext sollten sie niemals Whitespace-Zeichen zwischen Unterstrichen verwenden.

Makro	Wert	Bedeutung

__BCOPT__	1	Ist in jedem optimierenden Compiler definiert.

__BCPLUSPLUS__	0x340	Ist definiert, wenn C++ bei der Compilierung gewählt wird; Wert wird bei späteren Versionen größer sein.

__BORLANDC__	0x460	Versionsnummer

__CDECL__ 	1	Ist definiert, wenn die Option Aufrufkonvention auf C eingestellt ist; andernfalls nicht definiert.

_CHAR_UNSIGNED	1	Ist standardmäßig definiert und zeigt an, daß char standardmäßig vom Typ unsigned char ist. Mit Hilfe der -K Option kann dieses Makro deaktiviert werden.

__CONSOLE__	 Ist es definiert, so deutet es darauf hin, daß es sich um eine Textbildschirmanwendung handelt.

_CPPUNWIND	1	Aktiviert Stack-Abwicklung. Ist per Voreinstellung auf true gesetzt, kann mit -xd deaktiviert werden.

__cplusplus	1	Ist im C++ Modus definiert; sonst nicht definiert.

__DATE__	Stringliteral	Datum der ersten Bearbeitung der aktuellen Datei.

__DLL__	1	True, immer wenn -WD Option benutzt wird

__FILE__	Stringliteral	Name der aktuellen Datei, die bearbeitet wird.

__LINE__	Dezimale Konstante	Nummer der Zeile, die gerade bearbeitet wird.

_M_IX86	1	Ist immer definiert. Der Standardwert ist 300. Der Wert kann auf 400 oder 500 geändert werden, indem man die /4 oder /5 Option benutzt.

__MSDOS__	1 	int-Konstante

__MT__	1	Nur für den 32-Bit-Compiler definiert, wenn die -WM-Option verwendet wird. Legt fest, das die Mutlithread-Bibliothek gelinkt werden soll.

__PASCAL__ 	1	Ist definiert, wenn die Option Calling Convention auf Pascal gesetzt ist; andernfalls nicht definiert.

__STDC__	Konstante	definiert, wenn mit Option A compiliert, ansonsten undefiniert.

__TCPLUSPLUS__ 	0x310	Versionsnummer

__TEMPLATES__	1	Speziell für Borland C und C++ Compiler. 1 für C++ Dateien (Templates werden unterstützt), ansonsten undefiniert.

__TIME__	Stringliteral	Uhrzeit der ersten Bearbeitung der aktuellen Datei.

__TLS__	1	Bei Verwendung des 32-Bit Compilers immer wahr.

__TURBOC__ 	0x410	Wert wird bei späteren Versionen größer sein.

_WCHAR_T	1	Nur für C++ Programme definiert. Kennzeichnet wchar_t als intrinsisch definierten Datentyp.

_WCHAR_T_DEFINED	1	Nur für C++ Programme definiert. Kennzeichnet wchar_t als intrinsisch definierten Datentyp.

_Windows	Ist für 16- und 32-Bit Windows-Anwendungen definiert.

__WIN32__	1	Für den 32-Bit Compiler stets 1. Ist für Textbildschirm und GUI-Anwendungen definiert.

Hinweis:	__DATE__, __FILE__, __LINE__, __STDC__, and __TIME__ können nicht direkt hinter einer der Direktiven #define oder #undef folgen.


Exception-Behandlung in C++

Siehe auch

Dieses Kapitel beschreibt die Mechanismen der C++Builder Fehlerbehandlung, allgemein als Exception-Behandlung bezeichnet. Die Implementierung der C++ Exception-Behandlung ist mit der vorgeschlagenen ANSI-Spezifikation konsistent.

Die Mechanismen der Exception-Behandlung in C++Builder stellen Unterstützung für die Behandlung von Exceptions bereit, die während der Verwendung der Delphi-VCL auftreten können. Als Teil der Delphi-Unterstützung, stellt C++Builder auch Unterstützung für die Behandlung von Betriebssystem-Exceptions bereit.

Auslösen einer Exception

Behandeln einer Exception

Exception-Spezifikationen

Konstruktoren und Destruktoren bei der Exception-Behandlung

Nicht behandelte Exceptions

Setzen von Optionen für die Exception-Behandlung

Die Sprache C++ definiert einen Standard für die Exception-Behandlung. Dieser Standard stellt sicher, daß die Leistungsfähigkeit objektorientierter Entwicklung durch Ihr Programm genutzt wird.

In Übereinstimmung mit den Spezifikationen des ANSI/ISO -Entwurfes, unterstützt C++Builder das Terminierungsmodell für die Exception-Behandlung. Wenn eine anormale Situation zur Laufzeit auftritt, sollte das Programm terminieren. In jedem Fall gibt Ihnen das Auslösen von Exceptions die Möglichkeit, am Auslösepunkt Informationen zusammenzustellen, die für die Diagnose der Fehlerursache nützlich sein könnten. Ebenso können Sie in der Exception-Behandlungsroutine Aktionen angeben, die noch vor der Programmterminierung stattfinden sollen. Es werden ausschließlich synchrone Exceptions behandelt. Das bedeutet, daß die Fehlerursache innerhalb des Programms generiert wurde. Ein Ereignis wie z.B. Strg-C (das außerhalb des Programms generiert wurde), wird nicht als Exception betrachtet.

Exceptions können in C++ nur in try/catch-Konstrukten behandelt werden.

Syntax für ein try/catch-Konstrukt:

try-block:

   try compound-statement handler-list

handler-list:

   handler handler-listopt

handler:

   catch (exception-declaration) compound-statement

exception declaration:

   type-specifier-list declarator

   type-specifier-list abstract-declarator

   type-specifier-list

…

throw-expression:

   throw assignment-expression

Hinweis:	Die Schlüsselwörter catch und throw sind in einem C-Programm nicht erlaubt.

Der try-Block ist eine Anweisung, die den Programmfluß bei der Ausführung festlegt. Der try-Block wird mit dem Schlüsselwort try eingeleitet. Die Klammern nach dem Schlüsselwort werden verwendet, um einen Programmblock zu umgeben, der Exceptions generieren kann. Die Sprachstruktur legt fest, daß jede auftretende Exception innerhalb eine try-Blocks ausgelößt werden sollte.

Die Behandlungsroutine ist ein Quelltextblock, der dazu entworfen wurde, eine Exception zu behandeln. Die Sprache C++ erfordert es, daß zumnindest eine Behandlungsroutine direkt nach dem try-Block verfügbar ist. Es sollte für jede Exceptions, die das Programm generieren kann, eine Behandlungsroutine geben.

Wenn in einem Programm eine unvorhergesehene anormale Situation auftritt, können Sie die Kontrolle an einen anderen Programmteil übergeben, das für die Bearbeitung des Problems entwickelt wurde. Dies erfolgt über das Auslösen einer Exception.

Der Mechanismus für die Exception-Behandlung benötigt drei Schlüsselwörter: try, catch und throw. Auf den mit try angegebenen try-Block muß unmittelbar ein mit catch angegebener handler folgen. Wenn im try-Block eine Exception ausgelöst wird, wird die Programmkontrolle an die geeignete Exception-Behandlungsroutine übergeben. Das Programm sollte jede Exception auffangen, die von irgendeiner Funktion ausgelöst werden könnte. Andernfalls könnte das Programm anormal terminieren.


Exception-Deklarationen

Obwohl Exceptions in C++ von jedem beliebigen Typ sein können, ist es nützlich, aus Exceptions Objekte zu machen. Das Exception-Objekt wird behandelt, wie jedes andere Objekt auch. Eine Exception enthält Information von dem Punkt, von dem aus die Exception ausgelöst wurde bis zu dem Punkt, an dem die Exception abgefangen wurde. Dies ist die Information, die der Anwender wissen will, wenn beim Programm zur Laufzeit Fehler auftreten.


Auslösen einer Exception

Siehe auch	C++-Exception-Behandlung

Einem Quelltext-Block, in dem eine Exception vorkommen kann, muß das Schlüsselwort try vorangehen. Diesem Schlüsselwort try folgt der in geschweiften Klammern eingeschlossene Quelltext-Block. Dadurch wird angezeigt, daß das Programm darauf vorbereitet ist, auf die Existenz von Exceptions zu prüfen. Wenn eine Exception vorkommt, wird der Programmfluß unterbrochen und die folgenden Schritte eingeleitet:

Das Programm sucht nach einer passenden Behandlungsroutine.

Wenn eine Behandlungsroutine gefunden wurde, wird der Stack bis zu dieser Stelle abgebaut.

Die Programmkontrolle wird an die Behandlungsroutine übergeben.

Wenn keine Behandlungsroutine gefunden wurde, wird das Programm die Funktion terminate aufrufen. Wenn keine Exceptions ausgelöst wurden, läuft das Programm in der normalen Abfolge.

Ein throw -Ausdruck wird auch als Auslösepunkt bezeichnet. Sie können das Auslösen einer Exception unter Verwendung einer der unten aufgeführten Syntax-Spezifikationen angeben:

Beispiel 1

throw throw_object;

Im ersten Fall wird ein throw_object angegeben, das an die Behandlungsroutine übergeben wird.

Beispiel 2

throw;

Der zweite Fall gibt an, daß die aktuell behandelte Exception erneut ausgelöst wird. Dazu muß eine Exception aktuell existieren. Andernfalls wird die Funktion terminate aufgerufen.

Beispiel 3

void my_func1() throw (A, B)

{

    // Rumpf der Funktion.

}

Der dritte Fall gibt eine Liste mit Exceptions an, die my_func1 auslösen kann. Keine andere Exception sollte von my_func1 aus verbreitet werden. Wenn eine andere Exception als A oder B innerhalb von my_func1 generiert wird, wird sie als unerwartete Exception betrachtet und die Kontrolle an die Funktion unexpected übergeben.

Beispiel 4

void my_func2() throw ()

{

     // Rumpf der Funktion.

}

Der letzte Fall gibt an, daß my_func2 keine Exceptions auslösen sollte. Wenn eine andere Funktion (beispielsweise operator new) innerhalb des Rumpfes von my_func2 eine Exception auslöst, sollte eine solche Exception innerhalb des Blocks von my_funct2 abgefangen und behandelt werden. Ansonsten stellt eine solche Exception eine Verletzung der Exceptions-Spezifikationen für my_func2 dar. Die Funktion unexpected wird in diesem Fall aufgerufen.

Wenn eine Exception auftritt, initilaisiert der Ausdruck throw ein temporäres Objekt vom Typ T (passend zu Typ des Arguments arg) welches in throw(T arg) verwendet wird. Andere Kopien können nach Bedarf vom Compiler generiert werden. Konsequenterweise kann es sinnvoll sein, einen copy-Konstrutkor für das Exception-Objekt u definieren.


Behandeln einer Exception

Siehe auch	Beispiele	C++-Exception-Behandlung

Die Exception-Behandlungsroutine wird durch das Schlüsselwort catch angezeigt. Die Behandlungsroutine muß unmittelbar hinter den try-Block plaziert werden. Das Schlüsselwort catch kann auch unmittelbar nach einem anderen catch vorkommen. Jede Behandlungsroutine wird eine Exception nur dann behandeln, wenn sie mit dem in seiner Argumentliste angegebenen Typ übereinstimmt oder wenn sie zu diesem Typ konvertiert werden kann.

Jede vom Programm ausgelöste Exception muß von der Exception-Behandlungsroutine aufgefangen und behandelt werden. Wenn das Programm für eine ausgelöste Exception keine Exception-Behandlungsroutine bereitstellen kann, wird es terminate aufrufen.

Exception-Behandlungsroutinen werden in der Reihenfolge, in der sie auftreten, ausgewertet. Eine Exception ist aufgefangen, wenn ihr Typ mit dem in der catch-Anweisung enthaltenen übereinstimmt. Sobald eine Typ-Übereinstimmung vorliegt, wird die Programmkontrolle an die Behandlungsroutine übergeben. Nach Eintritt in die Behandlungsroutine werden die obersten Werte des Stacks entsprechend bereinigt. Die Behandlungsroutine legt die Aktionen für die Bearbeitung der Ausnahmesituation fest.

Eine goto-Anweisung kann benutzt werden, um die Programmkontrolle von einer Behandlungsroutine wieder zurückzugeben. Eine solche Anweisung kann allerdings niemals dafür benutzt werden, um in eine Behandlungsroutine zu gelangen.

Nach der Ausführung der Behandlungsroutine kann das Programm an der Stelle hinter der letzten Behandlungsroutine des aktuellen try-Blocks fortfahren. Keine anderen Behandlungsroutinen werden für die aktuelle Exception ausgewertet.


Beispiel 1

try {

   // Hier befindet sich der Quelltext, der eine Exception auslösen könnte
}

catch (T X) //  Bereitstellen einer Behandlungsroutine für jede im obigen Teil

           // eventuell ausgelöste Exception

{

  // Führe Aktionen durch

}

Dies Beispiel ist speziell definiert, um ein Objekt vom Typ T zu behandeln. Wenn das Argument ein T, T&, const T oder const T& ist, wird die Behandlungsroutine ein Objekt vom Typ X akzeptieren, sofern folgendes gilt:

T und X sind vom selben Typ

T ist eine zugreifbare Basisklasse für X im throw-Ausdruck.

T ist ein Zeigertyp und X ist ein Zeigertyp, der zu T durch eine Standardzeigerkonvertierung am Auslösepunkt konvertiert werden kann.


Beispiel 2

try {

   // Hier befindet sich der Quelltext, der eine Exception auslösen könnte

}

catch ( ... )

{

   // Führe Aktionen durch

}

Die Anweisung catch( ... ) wird jede Exception behandeln, unabhängig vom Typ. Wenn diese Anweisung verwendet wird, muß sie die letzte Behandlungsroutine für ihren try-Block sein.


Exception-Spezifikationen

Siehe auch	Beispiele	C++-Exception-Behandlung

Die Sprache C++ gibt Ihnen die Möglichkeit, beliebige Exceptions anzugeben, die den Aufruf einer Funktion auslösen können. Diese Exception-Spezifikation kann als Suffix zur Funktionsdeklaration verwendet werden. Die Syntax für eine Exception-Spezifikation ist wie folgt:

exception-Spezifikation:

   throw (type-id-listopt)

   type-id-List:

   type-id

   type-id-list, type-id

Das Funktionssuffix wird nicht als zum Funktionstyp zugehörig betrachtet. Damit wird ein Zeiger auf eine Funktion auch nicht von der Exception-Spezifikation der Funktion betroffen. Solch ein Zeiger überprüft nur die Rückgabe- und Argumenttypen der Funktion. Deshalb ist Folgendes legal:

void f2(void) throw();      // Sollte keine Exceptions auslösen

void f3(void) throw(BETA);  // Sollte nur Objekte vom Typ BETA auslösen

void (* fptr) ();           // Zeiger auf eine Funktion, die void
                          // zurückgibt

fptr = f2;

fptr = f3;

Eine besondere Sorgfalt sollten Sie beim Überschreiben virtueller Funktionen an den Tag legen. Es sei nochmals erwähnt, daß der Programmentwurf fehlerhaft werden kann, da die Exception-Spezifikation nicht als Bestandteil des Funktionstyps betrachtet wird.


Beispiel 1

Im folgenden Beispiel wird die abgeleitete Klasse BETA::vfunc so definiert, daß sie keine Exception auslöst - eine Abwandlung der ursprünglichen Funktionsdeklaration:

class ALPHA {

public:

   struct ALPHA_ERR {};

   virtual void vfunc(void) throw (ALPHA_ERR) {}; // Exception-Spezifikation

};

class BETA : public ALPHA {

   void vfunc(void) throw() {);  // Exception-Spezifikation wurde geändert!

};

Funktionsbeispiele mit Exception-Spezifikationen:

void f1();                // Die Funktion kann eine beliebige Exception auslösen.

void f2() throw();        // Die Funktion sollte keine Exception auslösen.

void f3() throw( A, B* ); // Die Funktion kann Exceptions auslösen,

                          // die von A öffentlich abgeleitet oder ein

                          // Zeiger auf öffentlich abgeleitetes B sind.

Die Definition und alle Deklarationen einer solchen Funktion muß eine Exception-Spezifikation besitzen, die dieselben Typ-IDs enthalten. Wenn eine Funktion eine Exception auslöst, die nicht in ihrer Spezifikation aufgelistet ist, wird das Programm unexpected aufrufen. Dies ist ein Laufzeitproblem und wird nicht zur Compile-Zeit ausgewiesen. Deshalb sollten Sie bei der Behandlung von Exceptions, die durch Elemente, die innerhalb einer Funktion aufgerufen wurden, sehr sorgfältig vorgehen.


Beispiel 2

// WIE MAN EXCEPTION-SPEZIFIKATIONEN ANLEGT UND ALLE EXCEPTIONS BEHANDELT

#include <iostream.h>

// EXCEPTION DEKLARATION

class Alpha {

   // Hier sollte der Quelltext stehen, der begründet, warum Sie diese
 // Exception Auslösen wollten

};

Alpha alpha_inst;

class Beta {

   // Hier sollte der Quelltext stehen, der begründet, warum Sie diese
 // Exception auslösen wollten

};

Beta beta_inst;

// MIT f3() SOLLEN NUR EXCEPTIONS VOM TYP Alpha ODER Beta AUSGELÖST WERDEN

void f3(char c) throw (Alpha, Beta) {

   cout << "f3() was called" << endl;

   if (c == 'a')

      throw( alpha_inst );

   if (c == 'b')

      throw( beta_inst );

   else ; // ANDERE EINGABEZEICHEN WERDEN IGNORIERT

}

// SOLLTE KEINE EXCEPTIONS AUSLÖSEN

void f2(char ch) throw() {

...try { // UMFASSE GESAMTEN QUELLTEXT IN EINEM TRY-BLOCK

      cout << "f2() was called" << endl;

      f3(ch);

   }

   // HIER FOLGEN DIE BEHANDLUNGSROUTINEN FÜR DIEJENIGEN EXCEPTIONS, VON
 // DENEN WIR WISSEN, DASS SIE AUSGELÖST WERDEN KÖNNEN

   catch (Alpha& alpha_inst) { cout << "Caught Alpha exception.";}

   catch (Beta& beta_inst) { cout << "Caught Beta exception.";}

   // WENN MEHR FUNKTIONEN HINZUGEFÜGT WERDEN, VON DENEN IRGENDEINE

   // EXCEPTIONS AUSLÖST, WIRD DIE FOLGENDE BEHANDLUNGSROUTINE

   // ALLE DIESE EXCEPTIONS AUFFANGEN.
 // WIR VERMEIDEN DAMIT EINE VERLETZUNG DER f2() THROW-SPEZIFIKATION

   catch ( ... ) {

      // ABER ZUR SICHERHEIT GEBEN WIR EINE WARNMELDUNG AUS

      cout << "Warning: f2() has elements with exceptions!" << endl;

   }

}

int main(void) {

char trigger;

   try {

      cout << "Input a character:";

      cin >> trigger;

      f2(trigger);

      cout << "\nSuccess.";

      return 0;  // AN DIESE STELLE KOMMEN WIR NUR, FALLS KEINE
               // UNVORHERGESEHENE EXCEPTION PASSIERT

   }

   catch ( ... ) {

      cout << "Need more handlers!";

      return 1;

      }

   }

Beispiel-Ausgabe, wenn "a" eingegeben wurde:

Input a character: a

f2() was called

f3() was called

Caught Alpha exception.

Success.


Beispiel 3

Wenn eine Exception ausgelöst wird, die nicht in der Exception-Spezifikation aufgelistet ist, wird die Funktion unexpected aufgerufen.

Die folgenden Beispiele verdeutlichen die Abfolge der Ereignisse, die auftreten können, wenn unexpected aufgerufen wurde. Das Verhalten ist davon abhängig, ob Sie eine Funktion mit set_unexpected() oder set_terminate() registrieren.

Programmverhalten, wenn eine Funktion mit set_unexpected() registriert ist:

unexpected() // AUTOMATISCH AUFGERUFEN

  |

  |

  |           // DEFINIEREN SIE IHRE UNEXPECTED-BEHANDLUNG

  |         unexpected_function my_unexpected( void )

  |         {

  |            // DEFINIEREN SIE DIE AUSZUFÜHRENDEN AKTIONEN

  |            // NEHMEN SIE EVENTUELL ANPASSUNGEN VOR

  |         }

  |

  |           // REGISTRIEREN SIE JETZT IHRE BEHANDLUNGSROUTINE

  |          set_unexpected( my_unexpected );

  |

my_unexpected();

Programmverhalten, wenn keine Funktion mit set_unexpected(), jedoch eine Funktion mit set_terminate() registriert ist.

unexpected()  // AUTOMATISCH AUFGERUFEN

  |

terminate()

  |

  |          // DEFINIEREN SIE IHR TERMINIERUNGS-SCHEMA

  |          terminate_function my_terminate( void )

  |             {

  |             // FÜHREN SIE AKTIONEN VOR DER TERMINIERUNG DURCH

  |             // SOLLTE KEINE EXCEPTIONS AUSLÖSEN

  |             exit(1); // MUSS IRGENDWIE BEENDET WERDEN

  |          }

  |

  |            // REGISTRIEREN SIE IHRE TERMINIERUNGSFUNKTION

  |            set_terminate( my_terminate );

  |

  |

my_terminate()

// PROGRAMM ENDET.


Konstruktoren und Destruktoren in der Behandlung von Exceptions

Siehe auch	C++-Exception-Behandlung

Wenn eine Exception ausgelöst wird, wird der Kopierkonstruktor für die Exception aufgerufen. Der Kopierkonstruktor dient zum Initialisieren eines temporären Objektes am Auslösepunkt. Andere Kopien können durch das Programm generiert werden.

Wenn der Programmfluß durch eine Exception unterbrochen wird, werden Destruktoren für alle automatischen Objekte aufgerufen, die seit dem Start des try-Blocks konstruiert wurden. Wenn die Exception während des Konstruierens eines Objektes ausgelöst wurde, werden die Destruktoren nur für die vollständig konstruierten Objekte aufgerufen. Wenn zum Beispiel ein Array von Objekten im Aufbau war, als eine Exception ausgelöst wurde, dann werden nur für die Array-Elemente Destruktoren aufgerufen, die bereits vollständig konstruiert wurden.

Wenn eine C++ Exception ausgelöst wird, erfolgt eine Stack-Abwicklung (stack unwinding). Stack-Abwicklungen treten ständig auf. Standardmäßig werden während der Stack-Abwicklung Destruktoren für automatische Objekte aufgerufen, Sie können diese Voreinstellung jedoch abschalten, indem Sie die Compiler-Option -xd verwenden.


Unbehandelte Exceptions

Siehe auch	C++-Exception-Behandlung

Wenn eine Exception ausgelöst und keine Behandlungsroutine gefunden wird, ruft das Programm die Funktion terminate auf. Das folgende Beispiel verdeutlicht eine mögliche eintretende Ereignisfolge, wenn ein Programm auf eine Exception trifft, für die es keine Behandlungsroutine gibt.

terminate();

  .

  .

  .

abort();  // PROGRAMM ENDET.


C-basierte strukturierte Exceptions

Siehe auch

C++Builder bietet eine Unterstützung zur Programmentwicklung, die sich der strukturierten Exceptions bedient. Sie können eine C-Quelltextdatei compilieren und linken, die eine Implementierung strukturierter Exceptions beinhaltet. In einem C-Programm können folgende ANSI-kompatible Schlüsselwörter für die Implementierung von strukturierten Exceptions verwendet werden: __except, __finally, und __try.

Anmerkung: Die Schlüsselwörter __finally und __try können nur in C-Programmen auftauchen.

Syntax der Ausnahmebehandlung mittels try-except:

Die Syntax der Implementierung einer Ausnahmebehandlung mittels try-except funktioniert wie folgt:

try-block:

  _ _try compound-statement (in a C module)

  try compound-statement (in a C++ module)

handler:

  _ _except (expression) compound-statement

Syntax für die Terminierung über try-finally

Die Syntax für die Implementierung einer Terminierung über try-finally lautet wie folgt:

try-block:

  _ _try compound-statement

termination:

  _ _finally compound-statement

Ihrer Win32-Dokumentation können Sie zusätzliche Details zur Implementierung von strukturierten Exceptions entnehmen.


Verwenden von C-basierten Exceptions in C++

Siehe auch	Beispiel

C++Builder erlaubt einen soliden Austausch zwischen Fehlerbehandlungsmechanismen von Delphi, C, und C++. Die folgenden Interaktionen werden zwischen C und C++ unterstützt:

Strukturierte Exceptions für C können in C++ Programmen verwendet werden.

C++ Exceptions können in einem C-Programm nicht verwendet werden, da die Behandlungsroutinen von C++ Exceptions durch das Schlüsselwort catch eingeleitet werden, catch aber in einem C-Programm nicht erlaubt ist.

Eine Exception, die durch einen Aufruf der Funktion RaiseException generiert wurde, wird durch einen try/__except oder __try/__except-Block behandelt. Alle Behandlungsroutinen von try/catch-Blöcken werden ignoriert, wenn RaiseException aufgerufen wird.

Die folgenden Hilfsfunktionen für C-Exceptions können sowohl in C- als auch in C++ Programmen verwendet werden:

GetExceptionCode

GetExceptionInformation

SetUnhandledExceptionFilter

UnhandledExceptionFilter

Für C++Builder ist es nicht erforderlich, daß die Funktion UnhandledExceptionFilter nur im Exception-Filter von __try/__except oder try/__except-Blöcken verwendet wird. Das Verhalten des Programms ist allerdings undefiniert, wenn diese Funktion außerhalb des __try/__except oder try/__except-Blockes aufgerufen wird.


Behandlung C-basierter Exceptions

Siehe auch	Beispiel

Die vollständige Funktionalität eines __except-Blockes ist in C++ erlaubt. Wenn eine Exception in einem C-Modul generiert wird, kann ein Behandlungsroutinenblock in einem separat aufrufenden C++ Modul bereitgestellt werden. Wenn keine Behandlungsroutine im aufrufenden Modul gefunden wird, wird das Programm standardmäßig terminiert.

Wenn eine Behandlungroutine für die generierte strukturierte Exception gefunden werden kann, können folgende Aktionen durchgeführt werden:

Führe die durch die Behandlungsroutine vorgegebenen Aktionen aus.

Ignoriere die generierte Exception und nimm die Programmausführung wieder auf.

Führe die Suche nach einer anderen Behandlungroutine fort (regeneriere die Exception).

Diese Aktionen sind mit dem Design von strukturierten Exceptions konsistent.

Das __try/__finally-Konstrukt stellt sicher, daß der Quelltext in einem __finally-Block ausgeführt wird, unabhängig davon, wie der Fluß innerhalb von __try existiert. Das Schlüsselwort __finally ist in einem C++ Programm nicht erlaubt und der __try/__finally-Block wird in einem C++ Programm nicht unterstützt.

Auch wenn der __try/__finally-Block in einem C++ Programm nicht unterstützt wird, so kann eine C-basierte Exception, die vom Betriebssystem oder vom Programm generiert wurde, dennoch mit einer geeigneten Stack-Abwicklung verbunden sein, indem innerhalb der Destruktoren lokale Objekte verwendet werden. Jedes mit der Compiler-Option -xd compilierte Modul wird Destruktoren haben, die für alle Objekte mit Speicherklasse auto aufgerufen werden. Stack-Abbau findet ab dem Punkt, wo die Exception ausgelöst wurde, bis zu dem Punkt, an dem die Exception aufgefangen wurde, statt.


Beispiel für C-basierte Exceptions in C++

/* In PROG.C */

void func(void) {

   .

   .

   .

   /* generieren Sie eine Exception */

   RaiseException( /* geben Sie Ihre Argumente an */ );

   .

   .

   .

}

// In CALLER.CPP

// Testen auf C++ oder C-basierten Exceptions.

#include <excpt.h>

#include <iostream.h>

int main(void) {

   try

   {            // Testen auf C++ Exceptions

      try

      {         // Testen auf C-basierten strukturierten Exceptions

         func();

      }

      __except( /* Filter-Ausdruck */ )

      {

      cout << "A structured exception was generated.";

   .

   .

   .

      /* Angabe auszuführender Aktionen für diese Exception */

      return -1;

      }

      return 0;

   }

   catch ( ... )

   {

   // Behandlung jeder C++ Exception

   cout << "A C++ exception was thrown.";

   return 1;

   }


Das PME-Modell

C++Builder-Anwendungen basieren auf dem PME-Programmiermodell (Property-Method-Event = Eigenschaft-Methode-Ereignis), in dem sich Anwendungen aus lauter einzelnen Komponenten zusammensetzen. Komponenten haben Eigenschaften, die ihren Status festlegen. Die Werte der Eigenschaften können geändert werden. Außerdem verfügen die Komponenten über Methoden (oder Elementfunktionen), mit denen sie manipuliert werden können. Wenn bestimmte Änderungen eintreten, lösen Komponenten Ereignisse aus. Diese Ereignisse werden an bestimmte Behandlungsroutinen übergeben, die im Quelltext der Komponente festgelegt sind.

Wo Programmierer bisher Klassen von Hand codiert oder sich aus der Microsoft Foundation Class (MFC) oder der ObjectWindows Library (OWL) bedient haben, können sie nun auf visuellem Wege mit der C++Builder-VCL (Visual Component Library = Bibliothek visueller Komponenten) arbeiten.

Die VCL kapselt fast alle Elemente, die in Win32-Anwendungen zum Einsatz kommen. Dazu gehören unter anderem

Komponenten der Benutzeroberfläche (Fenster, Steuerelemente, Menüs und Standarddialoge).

Komponenten für den Zugriff auf Datenbanken (von der dBase-Tabelle bis zur Oracle-Datenbank auf einem IBM-Großrechner).

Komponenten für bestimmte Operationen in Windows (Registrierdatenbank, Drucken, Multimedia).

Unterstützung erweiterter Windows-Funktionen (Multithreading, OLE-Automatisierung, Timer).

Unterstützung klassischer Datenstrukturen (Kollektionen und Listen).

Da diese Objekte nun über die VCL zur Verfügung stehen und die meisten davon auf visuellem Wege in der C++Builder-Benutzeroberfläche manipuliert werden können, brauchen C++ Programmierer sie nicht mehr manuell zu erzeugen oder zu bearbeiten. Eine C++Builder-Anwendung entsteht in aller Regel dadurch, daß Sie sie im Formular-Designer visuell entwerfen und den Ereignisbehandlungsroutinen der wichtigsten Komponenten ein wenig Code zuweisen. Halten Sie sich immer an folgende Regel: Verwenden Sie die Objekte der VCL, wann immer dies möglich ist, und schreiben Sie nur eigenen Code, wenn es sich nicht umgehen läßt.


Komponenten und Klassen

Auf den ersten Blick unterscheiden sich C++Builder-Komponenten nicht von anderen Klassen in C++. Dennoch gibt es Unterschiede zwischen diesen Komponenten und den Standardklassen, die von den meisten C++ Programmierern der alten Schule verwendet werden. Hier einige dieser Unterschiede:

Alle C++Builder-Komponenten sind Nachkommen von TComponent.

VCL-Komponenten werden normalerweise über ihre Eigenschaften manipuliert und so verwendet, wie sie sind. Sie dienen nicht als Basisklassen, von denen andere Klassen mit zusätzlichen oder veränderten Funktionen abgeleitet werden. Wenn eine Komponente von der VCL abgeleitet wird, geschieht dies in der Regel, um bereits vorhandenen Ereignisbehandlungsroutinen eigenen Code hinzuzufügen.

VCL-Komponenten belegen nur im Heap Speicher, nicht aber im Stack (d.h. sie müssen mit dem Operator new erzeugt werden).

Eigenschaften von Komponenten enthalten zur Laufzeit Typinformationen.

Komponenten können in die Komponentenpalette (ein Element der Benutzeroberfläche von C++ Builder) aufgenommen und im Formular-Designer bearbeitet werden.

Komponenten erreichen oft einen höheren Grad an Kapselung als Standardklassen in C++. Stellen Sie sich einen Dialog mit einer Schaltfläche vor. Wenn in einer C++ Windows-Anwendung ein Benutzer auf die Schaltfläche klickt, generiert das System eine WM_LBUTTONDOWN-Botschaft. Der Programmierer muß diese Botschaft abfangen. Er tut dies gewöhnlich in einer switch-Anweisung oder in einer Botschaftszuordnungs- bzw.Response-Tabelle, wo die Botschaft an die Routine weitergeleitet wird, die als Reaktion auf die Botschaft ausgeführt werden soll. Eine OWL-Anwendung ordnet beispielsweise mit folgendem Makro das Ereignis EV_BN_CLICKED (eine WM_LBUTTONDOWN-Botschaft), das von der Schaltfläche IDEVENTBUTTON im Dialog TEventTestDlgClient generiert wird, der Funktion EventBNClicked( ) zu:

DEFINE_RESPONSE_TABLE1(TEventTestDlgClient, TDialog)

//{{TEventTestDlgClientRSP_TBL_BEGIN}}

  EV_BN_CLICKED(IDEVENTBUTTON, EventBNClicked),

//{{TEventTestDlgClientRSP_TBL_END}}

END_RESPONSE_TABLE;

In C++Builder ist die Schaltfläche bereits so programmiert, daß sie mit einer integrierten Behandlungsroutine auf das Ereignis OnClick reagiert. Der Programmierer braucht dem Klick des Benutzers auf die Schaltfläche keine besondere Aufmerksamkeit zu widmen. Er stellt nur die Routine bereit, die als Reaktion auf den Mausklick aufgerufen wird, und ordnet sie mit Hilfe des Objektinspektors dem Ereignis OnClick zu.


Eigenschaften und Zuweisungs-/Abruffunktionen

Eigenschaften sind eine programmiersprachliche Neuerung in C++Builder. Sie erweitern die Zuweisungs- und/oder Abruffunktionen, die normalerweise zu C++ Klassen gehören. Bisher wurde eine Klasse beispielsweise so deklariert:

class Foo {

int howMany;

public:

Foo() {howMany = 0;};

void setValue(int n) {howMany = n;};

int getValue() {return howMany};

};

Nun lautet die entsprechende Deklaration so:

class Foo {

int howMany;

void setValue(int n) {howMany = n;};

int getValue() {return howMany;};

public:

Foo() {howMany = 0;};

__published:

__property int count = {read=getValue, write=setValue};

};

In beiden Klassen gibt es das private Datenfeld howMany. Beide Klassen bedienen sich einer Zuweisungs- (getValue) und einer Abruffunktion (setValue), um howMany einen Wert zuzuweisen bzw. diesen abzurufen. Bei der zweiten Klasse erfolgt der Zugriff auf den Wert von howMany jedoch dadurch, daß der Benutzer der Eigenschaft count einen Wert zuweist oder diesen liest. Die Verwendung der Eigenschaft count vereinfacht für den Benutzer den Umgang mit der Klasse. Am Status des Datenfeldes howMany ändert sich dadurch nichts.

Eigenschaften erlauben die Verwendung intelligenter Datenfelder in Klassen. Im obigen Beispiel könnte die Zuweisungsfunktion beispielsweise so aussehen:

void setValue(int n) {howMany = n < 0 ? 0 : n;};

Dadurch kann howMany kein Wert zugewiesen werden, der kleiner ist als 0. Ein Beispiel:

void Foobar()

{

Foo myFoo;

myFoo.count = 10;

int x = myFoo.count; 	// x wird 10 zugewiesen;

myFoo.count = -10;	// aus -10 wird bei dieser Zuweisung 0

x = myFoo.count;  	// x wird 0 zugewiesen;

}


Vorhandener Code

Siehe auch

Der Compiler von C++Builder kann Win32 C und C++ Code compilieren, der zu Borland C++ 5.0 kompatibel ist. C++Builder kann jedoch keine 16-Bit Windows- oder DOS-Programme compilieren.

Da C++Builder über eine einzigartige Exception-Behandlung verfügt, müssen Objektcode und Bibliotheksmodule, die ursprünglich mit Borland C++ 5.01 oder früher compiliert wurden, mit C++Builder neu compiliert werden, bevor sie in eine C++Builder-Anwendung gelinkt werden können. Umgekehrt können auch Objektmodule, die mit C++Builder compiliert wurden, nicht in Projekte gelinkt werden, die mit Borland C++ 5.01 oder früher compiliert wurden.

Zu C++Builder gehört eine nicht von der VCL abhängige Multithread-Laufzeitbibliothek (RTL), die die Verwendung von vorhandenem Code ermöglicht. Diese Bibliothek namens CW32MT.LIB unterstützt allerdings nicht die Möglichkeit, Betriebssystem-Exceptions abzufangen, da dies ohne Verwendung der VCL nicht möglich ist.

Inline-Assembler

Der Compiler von C++Builder verfügt über keinen integrierten Inline-Assembler. Mit dem Schlüsselwort asm (oder seinen Varianten) im C++ Quelltext können Sie den Compiler veranlassen, eine Assembler-Quelltextdatei (.ASM) Ihres Programms zu erzeugen, die Sie dann mit dem Turbo Assember 5.0 von Borland (separat erhältlich) assemblieren können.

Object Windows Library (OWL) und Microsoft Foundation Classes (MFC)

Obwohl OWL und MFC nicht zum Lieferumfang von C++Builder gehören, können OWL 5.0- und MFC 4.1-Anwendungen mit C++Builder compiliert werden. Zu diesem Zweck müssen die OWL- oder MFC-Bibliotheken mit dem Compiler von C++Builder neu compiliert werden. Für die OWL muß außerdem die Bibliothek CLASSLIB.LIB neu compiliert werden. Die MFC muß aus den Quelltexten, die mit Borland C++ 5.01 oder einer späteren Borland-Version ausgeliefert wurden, neu compiliert werden.

Achten Sie darauf, daß Sie die Borland C++ Versionen der Bibliotheken nicht überschreiben. Borland C++ und C++Builder benötigen jeweils ihre eigene, mit dem entsprechenden Compiler compilierte Version dieser Bibliotheken.

Einige OWL-Klassennamen konfligieren mit VCL-Klassennamen. Um zweideutige Deklarationen zu vermeiden, verwendet C++Builder Namespaces für VCL-Klassen.


Formulare im Speicher erzeugen
Formulare im Speicher erzeugen – Standardverhalten

VCL-Komponenten, wie Formulare und Steuerelemente, die mit der Borland C++Builder-IDE erstellt wurden, werden in programmtechnischem Sinn erzeugt, sobald die Anwendung gestartet wird, und freigegeben, wenn die Anwendung beendet wird. Für jedes Formular, das mit dem Formular-Designer erzeugt wurde, wird eine globale Variable mit demselben Namen wie der erzeugt, der im Formular-Designer festgelegt wurde. Diese Variable fungiert als Zeiger auf ein Objekt der Formularklasse und referenziert das Formular, während die Anwendung läuft. Alle Quelltextdateien (CPP), welche die Header-Datei (H) des Formulars einbinden, greifen über diesen Zeiger auf das Formular zu. Ebenso wird in die Funktion WinMain() der Anwendung entsprechender Code eingefügt, der den eigentlichen Aufbau des Formulars im Speicher übernimmt. Ob das Formular beim Start der Anwendung erzeugt wird oder nicht, hängt davon ab, ob das Formular in der Liste Automatische Formularerstellung der Registerkarte Formulare im Dialogfeld Projektoptionen aufgeführt ist. Unter der Voraussetzung, daß das Formular automatisch erzeugt wurde (Standard), existiert es während des gesamten Programmlaufs und kann zu beliebiger Zeit mit seinen Methoden Show() oder ShowModal() aufgerufen werden, falls es nicht bereits sichtbar ist.


Formulare zur Laufzeit erzeugen

Alle Objekte, die von einem Nachkommen von TObject abgeleitet wurden, etwa ein Formular, müssen auf dem Heap mit dem Operator new erstellt werden. Ein Beispiel:

TMyForm MyForm(NULL);	   // Fehler

TMyForm *MyForm = new TMyForm(NULL);  // OK

Als Benutzer sollten Sie den Operator new für VCL-basierte Klassen nicht überladen.

Wenn für den Zugriff auf das Formular mit new die globale Variable verwendet wird, die vom Formular-Designer definiert wurde, muß sichergestellt sein, daß das Formular nicht während der Initialisierung der Anwendung automatisch erzeugt wurde. Andernfalls wird Speicher vergeudet, und das automatisch erzeugte Formular ist nicht zugreifbar. Die folgende Beispielanwendung erläutert das Problem. Es gibt zwei Formulare: MainMForm (beim Start automatisch erzeugt und angezeigt) und ResultsForm (beim Start automatisch erzeugt, aber nicht angezeigt). Die Quelltextdatei hat folgenden Aufbau:

#include <vcl\vcl.h>

#pragma hdrstop

//---------------------------------------------------------------------

USEFORM("MainForm.cpp", MainMForm);

USERES("ParamTest.res");

USEFORM("Results.cpp", ResultsForm);

//---------------------------------------------------------------------

WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)

{

Application->Initialize();

  Application->CreateForm(__classid(TMainMForm), &MainMForm);

  Application->CreateForm(__classid(TResultsForm), &ResultsForm);

  Application->Run();

  return 0;

}

MainMForm und ResultsForm sind Zeigervariablen mit globalem Gültigkeitsbereich und verweisen jeweils auf das entsprechende Formular. Der C++Builder-Programmierer möchte eine modale Instanz von ResultsForm erzeugen, sobald der Benutzer auf eine Schaltfläche in MainMForm klickt. Dazu ist folgende Ereignisbehandlungsroutine vorgesehen:

void __fastcall TMainMForm::FirstButtonClick(TObject *Sender)

{

ResultsForm = new TResultsForm(this);	// SCHLECHT! Überschreibt globale

                                       	// Referenz!

  ResultsForm->ShowModal();

  delete ResultsForm;

}

In diesem Fall erzeugt der Programmierer eine neue Instanz von ResultsForm und überschreibt dabei die automatisch erzeugte Instanz. Diese Instanz ist zwar immer noch vorhanden, aber nicht mehr zugreifbar. Sobald die Ereignisbehandlungsroutine beendet ist, zeigt ResultsForm nicht mehr auf ein gültiges Formular. Alle Versuche, ResultsForm zu dereferenzieren, führen zu einem Absturz der Anwendung.

Modale Formulare

Wenn der Programmierer die globale Instanz von ResultsForm verwenden kann, muß die Ereignisbehandlungsroutine etwa so formuliert werden:

void __fastcall TMainMForm::FirstButtonClick(TObject *Sender)

{

ResultsForm->ShowModal();

}

In dieser Version wird das automatisch erzeugte Formular angezeigt, ohne daß eine neue Instanz entsteht bzw. die vorhandene aufgelöst wird, wenn die Ereignisbehandlungsroutine zurückkehrt.

Wenn tatsächlich eine neue Instanz des Formulars benötigt wird, gibt es dazu mehrere Möglichkeiten. Der Code in der ersten oben gezeigten Ereignisbehandlungsroutine (mit Operator new) funktioniert korrekt, wenn ResultsForm nicht automatisch beim Start der Anwendung erzeugt wird. Die automatische Erzeugung kann unterbunden werden, indem ResultsForm aus der Liste Automatische Formularerstellung in der Registerkarte Formulare des Dialogfeldes Projektoptionen entfernt wird. Alternativ dazu löschen Sie direkt in WinMain() folgende Zeile:

Application->CreateForm(__classid(TResultsForm), &ResultsForm);

Die Ereignisbehandlungsroutine im Beispiel löscht das Formular, sobald es geschlossen wird. Es wird also mit new nicht erneut instantiiert, wenn ResultForm an anderer Stelle in der Anwendung benötigt wird.

Ein zuverlässigeres Verfahren, eine eindeutige Instanz des modalen Formulars zu erzeugen, besteht darin, in der Ereignisbehandlungsroutine eine lokale Variable als Referenz auf die neue Instanz zu verwenden. Es spielt dann keine Rolle, ob ResultsForm automatisch erzeugt wurde oder nicht, weil die Ereignisbehandlungsroutine keine Referenz darauf enthält. Ein Beispiel:

void __fastcall TMainMForm::FirstButtonClick(TObject *Sender)

{

TResultsForm *rf = new TResultsForm(this);	// rf ist die lokale Instanz

  rf->ShowModal();

  delete rf;	// Instanz sicher aufgelöst

}

Beachten Sie, daß die globale Instanz des Formulars in dieser Version der Ereignisbehandlungsroutine nicht vorkommt.

In vielen Anwendungen werden nur die globalen Instanzen des Formulars verwendet. In allen Fällen, in denen eine neue modale Instanz eines Formulars erforderlich ist und die Verwendung des Formulars auf einen begrenzten Bereich der Anwendung beschränkt bleibt, etwa auf eine einzige Funktion, ist ein lokale Instanz die sicherste und effektivste Lösung.

Nicht modale Formulare

Wenn die erzeugte Formularinstanz nicht modal ist, kommt eine lokale Referenzvariable meist nicht in Frage. Die Methode Show(), mit der ein Formular nicht modal geöffnet wird, kehrt sofort zurück. Betrachten wir, was die folgende Ereignisbehandlungsroutine bewirkt:

void __fastcall TMainMForm::FirstButtonClick(TObject *Sender)

{

TResultsForm *rf = new TResultsForm(this);	// rf ist die lokale Instanz

  rf->Show();	// kehrt nach Öffnen zurück

  delete rf;	// SCHLECHT! Formular gelöscht.

}

In dem Moment, in dem das Formular von Show() erzeugt wird, wird es auch schon von der Anweisung delete wieder gelöscht. Noch schlechter wäre die Situation, wenn delete gar nicht in der Ereignisbehandlungsroutine enthalten wäre, denn dann würde die Referenzvariable rf sofort nach Erzeugung des Formulars den Gültigkeitsbereich verlassen, und das Formularobjekt würde nicht mehr zugreifbar sein.

Referenzvariablen für nicht modale Formulare müssen unbedingt genauso lange existieren wie das Formular. Das bedeutet, daß diese Variablen einen globalen Gültigkeitsbereich haben müssen. In den meisten Fällen kommt dazu die globale Variable zum Einsatz, die vom Formular-Designer erzeugt wurde. Der Variablenname stimmt hier mit der Eigenschaft Name des Formulars überein. Wenn zusätzliche Instanzen des Formulars erforderlich sind, sollten separate globale Variablen deklariert werden (Zeiger auf das Formularobjekt).


Parameter an Formulare übergeben

In der Regel werden die Formulare einer C++Builder-Anwendung mit dem Formular-Designer erzeugt. So erzeugte Formulare besitzen einen einzigen Konstruktor, der als einziges Argument TComponent* Owner entgegennimmt. Dabei handelt es sich um einen Zeiger auf den Eigentümer des erzeugten Formulars, also auf das aufrufende Anwendungsobjekt oder Formularobjekt. Das Argument kann den Wert NULL haben.

Um zusätzliche Argumente zu übergeben, wird ein zweiter Konstruktor benötigt. Außerdem muß das Formular mit dem Operator new instantiiert werden. Das Formular im folgenden Beispiel wurde mit dem Formular-Designer aufgebaut. Ein zweiter Konstruktor mit dem zusätzlichen Argument int whichButton wurde manuell hinzugefügt.

class TResultsForm : public TForm

{

__published:    // von der IDE erzeugte Komponenten

    TLabel *ResultsLabel;

    TButton *OKButton;

    void __fastcall OKButtonClick(TObject *Sender);

private:        // Benutzerdeklarationen

public:         // Benutzerdeklarationen

    virtual __fastcall TResultsForm(TComponent* Owner);

    virtual __fastcall TResultsForm(int whichButton, TComponent* Owner);

};

Ein Konstruktor könnte folgende Form haben. (Hier wird der Parameter int verwendet, um die Eigenschaft Caption eines Label-Objekts im Formular zu setzen.)

__fastcall TResultsForm::TResultsForm(int whichButton, TComponent* Owner)

  : TForm(Owner)

{

switch (whichButton) {

case 1:

ResultsLabel->Caption = "Erste Schaltfläche angeklickt!";

break;

case 2:

ResultsLabel->Caption = "Zweite Schaltfläche angeklickt!";

break;

case 3:

ResultsLabel->Caption = "Dritte Schaltfläche angeklickt!";

}

}

Wenn Sie ein Formular instantiieren, daß mehrere Konstruktoren besitzt, wählen Sie den Konstruktor, der für Ihre Situation am besten geeignet ist. Im folgenden Beispiel erzeugt die Behandlungsroutine für das Ereignis OnClick einer Schaltfläche eine Instanz von TResultsForm und verwendet dazu einen zusätzlichen Parameter:

void __fastcall TMainMForm::SecondButtonClick(TObject *Sender)

{

TResultsForm *rf = new TResultsForm(2, this);

rf->ShowModal();

delete rf;

}


Daten aus Formularen übernehmen

In der Praxis bestehen die meisten Anwendungen aus mehreren Formularen, und oft ist ein Informationsaustausch zwischen den Formularen notwendig. Die Weitergabe von Informationen erfolgt entweder dadurch, daß Parameter an den Konstruktor des Empfängerformulars übergeben werden oder daß die Eigenschaften des Formulars bestimmte Werte erhalten. Wenn Informationen dagegen aus einem Formular abgerufen werden sollen, stehen mehrere Verfahren zur Verfügung.

Nicht modale Formulare

Informationen aus nicht modalen Formularen erhält man ganz einfach über einen Aufruf der public-Elementfunktionen des Formulars oder über einen Lesezugriff auf seine Eigenschaften. Eine Anwendung soll beispielsweise ein modales Formular ColorForm mit einer Liste ColorListBox besitzen, welche als Einträge eine Reihe von Farbnamen enthält („Rot“, „Grün“, „Blau“ usw.). Der jeweils in ColorListBox ausgewählte String wird automatisch in der Eigenschaft CurrentColor gespeichert. Die Deklaration des Formulars sieht so aus:

class TColorForm : public TForm

{

__published:    // von der IDE erzeugte Komponenten

TListBox *ColorListBox;

void __fastcall ColorListBoxClick(TObject *Sender);

private:        // Benutzerdeklarationen

String getColor();

void   setColor(String);

String curColor;

public:         // Benutzerdeklarationen

virtual __fastcall TColorForm(TComponent* Owner);

__property String CurrentColor = {read=getColor, write=setColor};

};

Die Behandlungsroutine für das Ereignis OnClick der Liste hat den Namen ColorListBoxClick und setzt den Wert der Eigenschaft CurrentColor jedesmal neu, wenn eine neue Auswahl getroffen wird. Die Ereignisbehandlungsroutine liest den String mit dem Farbnamen aus der Liste und weist ihn der Eigenschaft CurrentColor zu. Die Zuweisungsfunktion setColor wird von CurrentColor verwendet, um den Eigenschaftswert in dem als private deklarierten Datenelement curColor zu speichern:

void __fastcall TColorForm::ColorListBoxClick(TObject *Sender)

{

int index = ColorListBox->ItemIndex;

if (index >= 0) {	// prüfen, ob wirklich eine Farbe gewählt ist

CurrentColor = ColorListBox->Items->Strings[index];

}

else	// keine Farbe ausgewählt

CurrentColor = "";

}

//---------------------------------------------------------------------

void TColorForm::setColor(String s)

{

curColor = s;

}

Nun soll angenommen werden, daß ein weiteres Formular namens ResultsForm immer dann die Information benötigt, welche Farbe aktuell ausgewählt ist, wenn eine Schaltfläche UpdateButton aktiviert wird. Die Behandlungsroutine für das Ereignis OnClick der Schaltfläche UpdateButton könnte diesen Aufbau haben:

void __fastcall TResultsForm::UpdateButtonClick(TObject *Sender)

{

if (ColorForm->Name == "ColorForm") {	// prüfen, ob ColorForm existiert

String s = ColorForm->CurrentColor;

// Farbnamen-String wird verarbeitet

}

}

Die Ereignisbehandlungsroutine überprüft zuerst den Wert der Eigenschaft Name des Formulars ColorForm, um sicherzustellen, daß es existiert. Die Abfrage von CurrentColor ruft eine einfache Abfragefunktion getColor auf:

String TColorForm::getColor()

{

return curColor;

}

Wäre die Funktion getColor des Formulars ColorForm als public deklariert, könnten auch andere Formulare die Farbe ermitteln, ohne den Umweg über eine Eigenschaftsabfrage nehmen zu müssen (z.B. String s = ColorForm->getColor();). Nichts hindert ein anderes Formular daran, die aktuell ausgewählte Farbe in ColorForm abzurufen. Dazu wird die Listenauswahl direkt abgefragt:

String s = ColorListBox->Items->Strings[ColorListBox->ItemIndex];

Allerdings bildet eine Eigenschaft eine sehr klare und einfache Schnittstelle zu ColorForm. Es muß nichts anderes bekannt sein, als daß ColorForm eine Eigenschaft CurrentColor besitzt, welche die gewünschte Information enthält.

Modale Formulare

Ebenso wie nicht modale Formulare enthalten auch modale Formulare oft Informationen, die in anderen Formularen von Bedeutung ist. Die häufigste Konstellation ist, daß Formular A ein Formular B öffnet. Wenn Formular B geschlossen wird, muß Formular A wissen, welche Aktionen der Benutzer in Formular B ausgeführt hat, um entsprechend zu reagieren. Solange sich Formular B noch im Speicher befindet, sind Lesezugriffe auf die Eigenschaften oder Elementfunktionen möglich, ganz genau wie oben für nicht modale Formulare beschrieben. Was aber ist, wenn Formular B beim Schließen aus dem Speicher gelöscht wurde? Ein Formular besitzt keinen expliziten Rückgabewert. Deshalb muß ein Weg gefunden werden, relevante Informationen rechtzeitig zu speichern.

Als Beispiel soll eine abgewandelte Version des bekannten Formulars ColorForm dienen, das ursprünglich als modales Formular entworfen wurde. Die Deklaration sieht so aus:

class TColorForm : public TForm

{

__published:    // von der IDE erzeugte Komponenten

TListBox *ColorListBox;

TButton *SelectButton;

TButton *CancelButton;

void __fastcall CancelButtonClick(TObject *Sender);

void __fastcall SelectButtonClick(TObject *Sender);

private:        // Benutzerdeklarationen

String* curColor;

public:         // Benutzerdeklarationen

virtual __fastcall TColorForm(TComponent* Owner);

virtual __fastcall TColorForm(String* s, TComponent* Owner);

};

Im Formular gibt es eine Liste ColorListBox mit den Namen verschiedener Farben. Eine Schaltfläche SelectButton bewirkt, daß die in ColorListBox ausgewählte Farbe vermerkt wird. Die Schaltfläche CancelButton dient einfach dazu, das Formular zu schließen.

Beachten Sie, daß der benutzerdefinierte Konstruktor ein String*-Argument entgegennimmt. Es wird vorausgesetzt, daß String* ein String ist, der dem aufrufenden Formular ColorForm bekannt ist. Der Konstruktor ist folgendermaßen implementiert:

__fastcall TColorForm::TColorForm(String* s, TComponent* Owner)

  : TForm(Owner)

{

curColor = s;

*curColor = "";

}

Der Zeiger auf das als private deklarierte Datenelement curColor wird gespeichert. Der String wird als leerer String initialisiert. Zu bemerken ist, daß das Formular mit dem Operator new erzeugt worden sein muß, um den benutzerdefinierten Konstruktor verwenden zu können. Vergewissern Sie sich, daß das Formular nicht beim Start der Anwendung automatisch erzeugt wurde, wenn Sie keine solche Version des Formular benötigen.

Es wird nun erwartet, daß der Benutzer eine Farbe aus der Liste wählt, zur Bestätigung auf SelectButton klickt und das Formular dann schließt. Die Ereignisbehandlungsroutine für OnClick der Schaltfläche SelectButton ist folgendermaßen definiert:

void __fastcall TColorForm::SelectButtonClick(TObject *Sender)

{

int index = ColorListBox->ItemIndex;

if (index >= 0)

*curColor = ColorListBox->Items->Strings[index];

Close();

}

Beachten Sie, daß die Ereignisbehandlungsroutine den ausgewählten Farbnamen unter der String-Adresse speichert, die dem Konstruktor übergeben wurde.

Um ColorForm wirklich einsetzen zu können, muß das aufrufende Formular eine solche String-Adresse an den Konstruktor übergeben. Angenommen, ColorForm wurde von einem Formular ResultsForm instantiiert, weil in ResultsForm eine Schaltfläche UpdateButton angeklickt wurde. Die Ereignisbehandlungsroutine müßte so aussehen:

void __fastcall TResultsForm::UpdateButtonClick(TObject *Sender)

{

String s;

GetColor(&s);

if (s != "") {

// Farbnamen-String verarbeiten

}

else {

// andere Aktionen, weil keine Farbe ausgewählt ist

}

}

//---------------------------------------------------------------------

void TResultsForm::GetColor(String *s)

{

ColorForm = new TColorForm(s, this);

ColorForm->ShowModal();

delete ColorForm;

}

Die Routine UpdateButtonClick erzeugt einen String s. Die Adresse von s wird der Funktion GetColor übergeben, die wiederum ColorForm erzeugt und dabei den Zeiger auf s als Argument an den Konstruktor übergibt. Sobald ColorForm geschlossen wird, wird es auch gelöscht. Allerdings bleibt der ausgewählte Farbname in s erhalten, vorausgesetzt, es wurde eine Auswahl getroffen. Andernfalls ist der String s leer und signalisiert, daß der Benutzer ColorForm ohne Auswahl verlassen hat.

In diesem einfachen Beispiel wurden die Informationen aus dem modalen Formular in einer einzigen String-Variablen abgelegt. Natürlich können dazu auch komplexere Objekte verwendet werden. Wichtig dabei ist, daß immer ein Mechanismus vorgesehen wird, mit dessen Hilfe das aufrufende Formular erkennt, daß das modale Formular geschlossen wurde, ohne daß eine Auswahl oder Änderung stattgefunden hat. Im Beispiel wurde der String s dazu auf einen leeren String voreingestellt.


Delphi-Formulare in C++Builder-Projekten

Siehe auch

Borland C++Builder enthält eine abgewandelte Version des Borland Delphi-Compilers, mit dem Delphi-Units und -Formulare compiliert und in C++Builder-Anwendungen gelinkt werden können.

Um eine Delphi-Unit in ein C++Builder-Projekt einzufügen, wählen Sie Zum Projekt hinzufügen im Menü Projekt oder klicken auf das Symbol „+“ im Fenster Projektverwaltung, um das Dialogfeld Zum Projekt hinzufügen zu öffnen. Stellen Sie Pascal-Unit als Dateityp ein, und markieren Sie die gewünschte Object Pascal-Datei (PAS).

Wenn Sie Object Pascal-Quelltextdateien aus einem Delphi-Projektverzeichnis in ein C++Builder-Projektverzeichnis kopieren, vergessen Sie nicht, die Formulardefinitionsdateien (DFM) sowie die Windows-Ressourcendateien (RES) ebenfalls zu kopieren.

Sobald das C++Builder-Projekt, das die Delphi-Unit(s) enthält, aufgebaut ist, werden die Delphi-Units automatisch mit der C++Builder-Version des Delphi-Compilers compiliert. Dieser erzeugt folgende Ausgaben:

1.	Eine compilierte Delphi-Unit (DCU), die in ein Delphi-Projekt gelinkt werden kann.

2.	Eine C++ Header-Datei (HPP), welche die C++ Äquivalente der Object Pascal-Klassendeklarationen enthält, die aus dem Pascal-Quelltext stammen.

3.	Eine Objektdatei (.OBJ), die formatkompatibel zu C++Builder ist. Besondere Beachtung verdienen übernommene Konstruktoren..

Die C++ Header-Dateien, die der Compiler für die Delphi-Units erzeugt, können mit #include in alle C++ Quelltextdateien eingebunden werden, die auf Klassen basieren, die in Delphi-Units deklariert sind. (In Delphi werden Klassen normalerweise in derselben Quelltextdatei deklariert und definiert. In C++ dagegen erfolgt die Deklaration in einer Header-Datei und die Definition in einer Quelltextdatei.)

C++Builder unterstützt praktisch alle intrinsischen Delphi-Datentypen, wobei C++Builder-Code auch die compilierten Delphi-Units verwenden kann. Wenn Sie Delphi-Methoden in C++ aufrufen, verwenden Sie immer die Aufrufkonvention __fastcall, wenn die Delphi-Methoden nicht explizit für eine andere Aufrufmethode vorgesehen sind.


Übernommene Konstruktoren

Siehe auch

Wenn Object Pascal-Quelltext konventionsgerecht für C++ compiliert werden soll, muß ein Detail besonders beachtet werden: Klassenkonstruktoren in Object Pascal können mit Namen deklariert werden, die nicht mit dem Klassennamen übereinstimmen. Beispielsweise kann es zu einer Klasse Base einen Konstruktor Create() geben.

Wenn die Klasse Base nun in ein Format gebracht werden soll, das den Normen von C++ entspricht, muß der Konstruktor umbenannt werden. Im Beispiel hieße das, Base.Create() wird im C++ Quelltext zu myClass::myClass().

Wenn Objekte von Base abgeleitet werden, wird der übernommene Konstruktor (mit dem entsprechenden Gültigkeitsbereich) zur erbenden Klasse hinzugefügt. Object Pascal-Konstruktoren behalten ihren Gültigkeitsbereich bei. Im Detail heißt dies, public-Konstruktoren werden dem public-Abschnitt der erbenden Klasse zugeordnet und protected-Konstruktoren den protected Abschnitten.

class myClass : public Base {

public:

   myClass( float f) {};

   myClass() {};        // Das war einmal Base.Create().

};

Falls Base einen zweiten Konstruktor besitzt, wird Base.AnotherCreate() ebenfalls umbenannt und in die C++ Klasse übernommen. Nach der Umbenennung ist der Konstruktor nicht mehr vom ersten zu unterscheiden. Um dies zu beheben, muß der Konstruktor ein Argument erhalten.

class myClass : public Base {

public:

   myClass( float f) {};

   myClass() {};        // Das war einmal Base.Create().

   myClass(int =0) {};  // Hier muß manuell korrigiert werden.

};

Wenn Base von FirstBase mit dem protected-Konstruktor FirstBase.FirstBaseCreate(i Integer) abgeleitet ist, muß ihr Konstruktor ebenfalls in myClass geschrieben werden. Beachten Sie, daß FirstBase eine Argumentliste enthält, womit die Unterscheidung von einem anderen myClass-Konstruktor unmöglich wird.

class myClass : public Base {  // Base erbt von FirstBase.

public:

   myClass( float f) {};

   myClass() {};        // Das war einmal Base.Create()

   myClass(int =0) {};  // Hier muß manuell korrigiert werden.

protected:

   myClass(int i; int = 0); // Hier muß manuell korrigiert werden.

};

Damit Sie als Programmierer die Beziehungen zwischen alten und neuen Konstruktoren erkennen können, also zwischen Object Pascal und C++, werden bei der Neucompilierung Kommentare in die HPP-Datei geschrieben.


Rückgabetypen von Delphi-Funktionen

Siehe auch

In C++Builder kann es zu Problemen mit bestehendem Delphi-Quelltext kommen. Pascal-Funktionen können ein Array zurückgeben.

  unit rarr;

  interface

  type

    T = array[0..99] of char;

  function f : T;

  implementation

  function f : T;

  var

    localArray : T;

  begin

    f := localArray;

  end;

  end.

DCC32 -jphn erzeugt eine HPP-Datei mit dem folgenden Prototyp:

   extern T __fastcall f(void);

Es ist zwar möglich, die Deklaration in der HPP-Datei zu ändern, aber trotzdem besser, den Pascal-Quelltext umzuschreiben. Der Grund dafür ist, daß die HPP-Datei neu generiert werden könnte, wobei alle manuellen Änderungen verloren gingen. Außerdem erzeugt eine angepaßte Pascal-Quelltextdatei immer eine gültige OBJ-Datei, die erfolgreich gelinkt werden kann.

In C++ können Funktionen kein Array zurückgeben. Ändern Sie daher im Delphi-Quelltext die Funktion in eine Prozedur, die das Array als var-Parameter übernimmt.

procedure f(var cArray : T);

  var

    T localArray;

  begin

    // f := localArray;   // Ändern Sie diesen Zustand

    cArray := localArray; // in diesen.

  end.


Virtuelle Funktionen portieren

Siehe auch

Um beim Portieren der virtual- und override-Funktionen von Delphi nach C++Builder die semantische Grammatik der Delphi-Programmierung beizubehalten, verwenden die Compiler das Makro HIDESBASE, das in VCL\SYSDEFS.H als __declspec(hidesbase) definiert ist.

In Delphi kann eine virtuelle Funktion in einer Basisklasse deklariert werden. Derselbe Funktionsname kann in einer abgeleiteten Klasse enthalten sein und dort eine völlig neue Funktion bezeichnen, die in keiner Beziehung zu der früheren steht:

class T1

   procedure func; virtual;

END

class T2 (T1)

   procedure func;

END

DCC32 -jphn erzeugt eine HPP-Datei mit dem folgenden Prototyp:

   virtual void T1::func(void);

   HIDESBASE void T2::func(void);

Das Makro HIDESBASE bewirkt, daß die beiden Deklarationen der Funktion func vollkommen getrennt voneinander existieren. Ohne den Zusatz HIDESBASE würde die Semantik von C++ bewirken, daß die virtuelle Funktion T1::func() von T2::func()überschrieben wird.


Was ist eine DLL?

DLLs (Dynamic Link Library = dynamische Linkbibliothek) stellen eine Möglichkeit dar, Anwendungen in Module aufzuteilen. Die in den einzelnen Modulen enthaltenen Funktionen können bequem aktualisiert und wiederverwendet werden. DLLs tragen dazu bei, den Speicheraufwand zu reduzieren, da sie von mehreren Anwendungen zur selben Zeit verwendet werden können. Obwohl jede Anwendung eine eigene Kopie der Daten erhält, liegt der Code nur in einer einzigen Ausführung vor.

Unter Windows ist eine DLL ein Modul, das Funktionen und Daten enthält. Eine DLL wird zur Laufzeit vom aufrufenden Modul (EXE oder DLL) geladen. Wenn eine DLL geladen wird, wird sie dem Adreßraum des aufrufenden Prozesses zugeordnet.

DLLs können zwei Arten von Funktionen definieren: exportierte und interne Funktionen. Exportierte Funktionen können von anderen Modulen aufgerufen werden. Interne Funktionen können nur innerhalb der DLL aufgerufen werden, in der sie definiert sind.

Detaillierte Informationen darüber, wie DLLs generiert und verwendet werden, enthält die Microsoft® Win32 SDK-Referenz.


DLLs in C++Builder

Eine Windows-DLL kann von einer C++Builder-Anwendung wie von jeder anderen C++ Anwendung verwendet werden.

Um eine DLL beim Start Ihrer C++Builder-Anwendung statisch zu laden, linken Sie die entsprechende Importbibliothek zu Ihrer C++Builder-Anwendung. Um einer C++Builder-Anwendung eine Importbibliothek hinzuzufügen, öffnen Sie die Make-Datei (.mak) der Anwendung und fügen der Dateiliste der Variable ALLLIB den Namen der Importbibliothek hinzu. Falls nötig, erweitern Sie die Pfadliste für die Option -L der Variable LFLAGS (Linkeroptionen) um den Pfad der Importbibliothek. Die exportierten Funktionen der betreffenden DLL können dann von Ihrer Anwendung verwendet werden. Deklarieren Sie den Prototyp der DLL-Funktionen, die in Ihrer Anwendung verwendet werden, mit dem Modifizierer __declspec(dllimport):

__declspec(dllimport) return_type imported_function_name(parameters);

Um eine DLL während der Ausführung einer C++Builder-Anwendung dynamisch zu laden, verwenden Sie die API-Funktion LoadLibrary(). Mit der API-Funktion GetProcAddress() erhalten Sie Zeiger auf die einzelnen Funktionen, die Sie verwenden wollen.

Weitere Informationen über DLLs finden Sie in der Microsoft® Win32 SDK-Referenz.


In C++Builder DLLs generieren

Beim Generieren von DLLs gibt es keine besonderen Unterschiede zwischen C++Builder und Standard C++. Exportierte Funktionen sollten wie auch in Borland C++ oder Microsoft Visual C++ mit dem Modifizierer __declspec (dllexport) bezeichnet werden. Der folgende Code stellt beispielsweise für C++Builder und andere Windows C++ Compiler ein gültiges Konstrukt dar:

// MyDLL.cpp

double dblValue(double);

double halfValue(double);

extern „C“ __declspec(dllexport) double changeValue(double, bool);

double dblValue(double value)

{

return value * value;

};

double halfValue(double value)

{

return value / 2.0;

}

double changeValue(double value, bool whichOp)

{

return whichOp ? dblValue(value) : halfValue(value);

}

Dieser Code exportiert die Funktion changeValue und macht sie für aufrufende Anwendungen verfügbar. Die Funktionen dblValue und halfValue sind dagegen intern und können von außerhalb der DLL nicht aufgerufen werden.

In der C++Builder-Entwicklungsumgebung können Sie ein neues DLL-Projekt anlegen, indem Sie Datei/Neu wählen und auf das DLL-Symbol doppelklicken. Dadurch öffnet sich ein neues Fenster im Editor, und die Projektoptionen werden so gesetzt, daß anstelle einer EXE- eine DLL-Datei erzeugt wird. Beachten Sie, daß im neuen Quelltext standardmäßig VCL.H eingebunden wird. Wenn Ihre DLL keine VCL-Komponenten verwendet, können Sie diese Zeile entfernen.

Weitere Informationen über DLLs finden Sie in der Microsoft® Win32 SDK-Referenz.


DLLs compilieren

Um in der C++Builder-Entwicklungsumgebung eine DLL zu compilieren und zu linken, setzen Sie die Option Anwendungsplattform der Registerkarte Linker im Dialog Projektoptionen auf DLL generieren und compilieren das Projekt wie gewohnt.

Wenn Sie den Kommandozeilen-Compiler verwenden, rufen Sie den Linker (entweder TLINK32.EXE oder ILINK32.EXE) mit der Option -Tpd auf. Ein Beispiel:

tlink32 /c /aa /Tpd c0d32.obj mydll.obj, mydll.dll, mdll.map, import32.lib cw32mt.lib

Soll die DLL von anderen C oder C++ Programmen verwendet und beim Start der Anwendung automatisch geladen werden, erzeugen Sie mit dem Kommandozeilen-Dienstprogramm IMPLIB.EXE eine Importbibliothek für die DLL, die zu den aufrufenden Anwendungen gelinkt werden kann. Ein Beispiel:

implib mydll.lib mydll.dll


DLLs generieren, die VCL-Komponenten enthalten

Einer der großen Vorteile von DLLs besteht darin, daß sie meist auch von Anwendungen verwendet werden können, die mit einem völlig anderen Entwicklungswerkzeug erstellt wurden. Wenn Ihre DLL VCL-Komponenten enthält (z.B. Formulare), die von der aufrufenden Anwendung verwendet werden sollen, müssen Sie exportierte Schnittstellenfunktionen bereitstellen. Diese Funktionen verwenden die Standard-Aufrufkonventionen und vermeiden Probleme mit C++ Namen. So funktioniert die aufrufende Anwendung auch dann, wenn sie die VCL nicht unterstützt.

Nehmen wir beispielsweise an, Sie wollen eine DLL generieren, um das folgende einfache Dialogfeld anzuzeigen:

Der Code für diese DLL lautet wie folgt:

// DLLMAIN.H

//---------------------------------------------------------------------

#ifndef dllMainH

#define dllMainH

//---------------------------------------------------------------------

#include <vcl\Classes.hpp>

#include <vcl\Controls.hpp>

#include <vcl\StdCtrls.hpp>

#include <vcl\Forms.hpp>

//---------------------------------------------------------------------

class TYesNoDialog : public TForm

{

__published:    // von der IDE verwaltete Komponenten

    TLabel *LabelText;

    TButton *YesButton;

    TButton *NoButton;

    void __fastcall YesButtonClick(TObject *Sender);

    void __fastcall NoButtonClick(TObject *Sender);

private:        // Benutzerdeklarationen

    bool returnValue;

public:         // Benutzerdeklarationen

    virtual __fastcall TYesNoDialog(TComponent* Owner);

    bool __fastcall GetReturnValue();

};

// die exportierte Schnittstellenfunktion

extern "C" __declspec(dllexport) bool InvokeYesNoDialog();

//---------------------------------------------------------------------

  extern TYesNoDialog *YesNoDialog;

//---------------------------------------------------------------------

#endif

// DLLMAIN.CPP

//---------------------------------------------------------------------

#include <vcl\vcl.h>

#pragma hdrstop

#include "dllMain.h"

//---------------------------------------------------------------------

#pragma resource "*.dfm"

TYesNoDialog *YesNoDialog;

//---------------------------------------------------------------------

__fastcall TYesNoDialog::TYesNoDialog(TComponent* Owner)

  : TForm(Owner)

{

   returnValue = false;

}

//---------------------------------------------------------------------

void __fastcall TYesNoDialog::YesButtonClick(TObject *Sender)

{

    returnValue = true;

    Close();

}

//---------------------------------------------------------------------

void __fastcall TYesNoDialog::NoButtonClick(TObject *Sender)

{

    returnValue = false;

    Close();

}

//---------------------------------------------------------------------

bool __fastcall TYesNoDialog::GetReturnValue()

{

    return returnValue;

}

//---------------------------------------------------------------------

// exportierte Standard C++ Schnittstellenfunktion, die die VCL aufruft

bool InvokeYesNoDialog()

{

    bool returnValue;

TYesNoDialog *YesNoDialog = new TYesNoDialog(NULL);

    YesNoDialog->ShowModal();

returnValue = YesNoDialog->GetReturnValue();

delete YesNoDialog;

    return returnValue;

}

//---------------------------------------------------------------------

Dieser Code zeigt den Dialog an und weist dem als private deklarierten Datenelement returnValue den Wert True zu, wenn die Schaltfläche OK gedrückt wurde. Andernfalls wird returnValue auf False gesetzt. Die als public deklarierte Funktion GetReturnValue() ermittelt den aktuellen Wert von returnValue.

Um den Dialog anzuzeigen und festzustellen, welche Schaltfläche gedrückt wurde, ruft die Anwendung die exportierte Funktion InvokeYesNoDialog() auf. Diese Funktion ist in DLLMAIN.H deklariert. Sie wird im C-Stil gelinkt (um Probleme mit C++ Namen zu vermeiden) und verwendet die Standard-C Aufrufkonvention. Die Funktion ist in DLLMAIN.CPP definiert.

Durch die Verwendung einer Standard-C Funktion als Schnittstelle zur DLL kann jede Anwendung, ob sie nun mit C++Builder erzeugt wurde oder nicht, die DLL verwenden. Die VCL-Funktionen, die zur Unterstützung des Dialogs benötigt werden, werden zur DLL selbst gelinkt. Die aufrufende Anwendung braucht nichts darüber zu wissen.

Wenn Sie eine DLL erzeugen, die die VCL verwendet, werden die benötigten VCL-Komponenten zur DLL gelinkt. Sie können den daraus resultierenden zusätzlichen Aufwand in Grenzen halten, indem Sie mehrere Komponenten in einer DLL zusammenfassen, da dann nur eine Kopie der VCL-Komponenten benötigt wird.


DLLs und String-Objekte

Wenn eine DLL exportierte Funktionen enthält, die String-Objekte (bzw. AnsiString-Objekte) entweder als Parameter erwarten oder zurückgeben, gelten folgende Regeln (auch dann, wenn diese String-Objekte in Objekte oder Strukturen eingebunden sind):

Die Header-Datei SHAREMEM.HPP muß sowohl in die Quelldatei(en) der DLL als auch in die Quelldatei(en) der ausführbaren Datei, die die Funktionen aufruft, eingebunden werden.

Die Datei BCBMM.DLL (enthält Verwaltungsroutinen für gemeinsam genutzten Speicher) muß zusammen mit der DLL weitergegeben werden.

Verwenden Sie Standard-C++ Strings anstelle von String-Objekten, wenn Ihre DLL ohne BCBMM.DLL auskommen soll.


Implementierungsabhängige ANSI-Standards

Bestimmte Aspekte des Standards ANSI C sind von ANSI nicht genau definiert. Statt dessen können diese Aspekte von Benutzern, die einen C Compiler implementieren, nach eigenen Wünschen definiert werden. Im folgenden wird dargelegt, wie diese implementierungsabhängigen Standards von Borland definiert wurden. Die Abschnittsnummerierungen beziehen sich auf den ANSI-Standard vom Februar 1990. Beachten Sie, daß C und C++ Unterschiede aufweisen; dieses Thema enthält nur Informationen C.

2.1.1.3 Kennzeichnen von Diagnosen.

Wenn der Compiler mit der richtigen Zusammenstellung von Optionen ausgeführt wird, sind alle von ihm ausgegebenen Meldungen, die mit den Worten Fatal, Error oder Warning beginnen, Diagnosen im von ANSI spezifizierten Sinn. Im folgenden sind die Optionen aufgeführt, die angegeben sein müssen, damit diese Interpretation zutrifft:

Benötigte Optionen für ANSI-Entsprechung
Option	Aktion

–A	Nur ANSI-Schlüsselwörter aktivieren.

–C–	Verschachtelte Kommentare nicht zulässig.

–i32	Mindestens 32 signifikante Zeichen in Bezeichnern.

–p–	Aufrufkonventionen von C verwenden.

–w–	Alle Warnungen, außer der folgenden, ausschalten.

–wbei	Warnung für nicht geeignete Initializer einschalten.

–wbig	Warnung vor zu großen Konstanten einschalten.

–wcpt	Warnung für nicht portierbare Zeigervergleiche einschalten.

–wdcl	Warnung für Deklarationen ohne Typ oder Speicherklasse einschalten.

–wdup	Warnung für doppelte, nicht gleiche Makrodefinitionen einschalten.

–wext	Warnung für Variablen, die sowohl extern als auch statisch deklariert sind, einschalten.

–wfdt	Warnung für Funktionsdefinitionen mit Typdefinition einschalten.

–wrpt	Warnmeldung für nicht portierbare Zeigerkonvertierung einschalten.

–wstu	Warnmeldung für nicht definierte Strukturen einschalten.

–wsus	Warnmeldung für fragwürdige Zeigerkonvertierung einschalten.

–wucp	Warnmeldung für Zeiger, die auf beide Datentypen signed und unsigned char gleichzeitig zeigen.

–wvrt	Warnung zu void-Funktionen, die einen Wert zurückgeben, einschalten.

Die folgenden Optionen können nicht verwendet werden:

–ms!	SS muß für die Datenmodelle Small mit DS übereinstimmen.

–mm!	SS muß für die Datenmodelle Small mit DS übereinstimmen.

–mt!	SS muß für die Datenmodelle Small mit DS übereinstimmen.

–zGxx	Der BSS-Gruppenname kann nicht geändert werden.

–zSxx	Der Datengruppenname kann nicht geändert werden.

Alle anderen Optionen, die hier nicht gesondert erwähnt wurden, können beliebig eingestellt werden.

2.1.2.2.1 Semantik der an main übergebenen Argumente.

Der Wert von argv[0] zeigt bei Programmen unter DOS-Versionen vor 3.0 auf ein Nullbyte. Bei DOS Version 3.0 oder höher zeigt argv[0] auf den Programmnamen.

Die restlichen argv-Strings zeigen auf die einzelnen Komponenten der an der DOS-Kommandozeile eingegebenen Argumente. Whitespace-Zeichen zur Trennung von Argumenten werden entfernt und alle Sequenzen aus zusammenhängenden nicht Whitespace-Zeichen werden als einzelnes Argument behandelt. Zeichenketten in Anführungszeichen werden normal verarbeitet (d.h. als einzelner String mit Leerzeichen).

2.1.2.3 Was zeichnet ein interaktives  Gerät aus.

Alle Geräte, die wie die Konsole aussehen, sind interaktiv.

2.2.1 Die Dateizuordnungssequenz des Ausführungszeichensatzes.

Die Dateizuordnungssequenz für den Ausführungszeichensatz verwendet den vorzeichenbehafteten Wert des Zeichens in ASCII.

2.2.1 Elemente des Zeichensatzes von Quelltext und Ausführung.

Als Quell- und Ausführungszeichensatz dient der  erweiterte ASCII-Satz, der von IBM PCs unterstützt wird. Alle Zeichen außer Strg+Z können in String-Literalen, Zeichenkonstanten oder Kommentaren verwendet werden.

2.2.1.2 Multibyte-Zeichen.

Multibyte-Zeichen werden in C++Builder unterstützt.

2.2.2 Druckrichtung.

Der Druckvorgang wird von links nach rechts, d.h. in der auf dem PC üblichen Richtung ausgeführt.

2.2.4.2 Anzahl der Bits pro Zeichen im Ausführungszeichensatz.

Der Ausführungszeichensatz enthält 8 Bit pro Zeichen.

3.1.2 Wieviele Anfangszeichen sind in Bezeichnern signifikant?

Die ersten 32 Zeichen sind signifikant. Diese Zahl kann jedoch über die Option (–i) an der Kommandozeile geändert werden. Interne und externe Bezeichner verwenden die gleiche Zahl an wertigen Zeichen. (In C++ ist die Anzahl der wertigen Zeichen unbeschränkt.)

3.1.2 Unterscheidung von Groß- und Kleinschreibung in externen Bezeichnern.

Der Compiler zwingt den Linker normalerweise zwischen Groß- und Kleinschreibung zu unterscheiden. Mit der Option (–l–c) an der Kommandozeile kann diese Unterscheidung unterdrückt werden.

3.1.2.5 Darstellung und Wertebereich verschiedener Integer-Typen.

Tabelle 2	Kennzeichnende Diagnosen in C++

Typ	16-Bit- Mindestwert	16-Bit-  Maximalwert	32-Bit-  Mindestwert	32-Bit- Maximalwert
signed char	–128	127	–128	127
unsigned char	0	255	0	255
signed short	–32,768	32,767	–32,768	32,767
unsigned short	0	65,535	0	65,535
signed int	–32,768	32,767	–2,147,483,648	–2,147,483,647
unsigned int	0	65,535	0	4,294,967,295
signed long	–2,147,483,648	2,147,483,647	–2,147,483,648	2,147,483,647
unsigned long	0	4,294,967,295	0	4,294,967,295

Alle char-Typen verwenden ein Byte von 8-Bit als Speicher.

All short types use 2 bytes, whether in a 16- or 32-bit program.

Alle short- und int-Typen verwenden 2 Bytes (in 16-Bit-Programmen).

Alle int-Typen verwenden 4 Bytes (in 32-Bit-Programmen).

Alle long-Typen verwenden 4 Bytes.

Falls Ausrichtung erforderlich ist (–a), werden alle Objekte vom Typ nonchar integer auf gerade Bytebegrenzungen ausgerichtet. Wenn die Ausrichtung –a4 erfordert wird, ist das Ergebnis eine 4-Byte-Ausrichtung. Zeichentypen werden niemals ausgerichtet.

3.1.2.5 Darstellung und Wertebereich der verschiedenen Gleitkommazahl-Typen.

Alle Gleitkomma-Datentypen in C++Builder richten sich nach den vom IEEE definierten Gleitkommaformaten. Der Datentyp float richtet sich nach dem vom IEEE spezifizierten 32-Bit-Format mit Nachkommastellen. Der Datentyp double verwendet das 64-Bit-Format mit Nachkommastellen nach IEEE. Der Datentyp long double richtet sich nach dem erweiterten 80-Bit-Format mit Nachkommastellen nach IEEE.

3.1.3.4 Zuordnung von Quell- und Ausführungszeichensätzen.

Die Zeichen in String-Literalen oder Zeichenkonstanten werden im ausführenden Programm nicht verändert. Quell- und Ausführungszeichensatz sind gleich.

3.1.3.4 Wert einer ganzzahligen Zeichenkonstante mit einer Zeichen- oder Escape-Sequenz, die im grundlegenden Ausführungszeichensatz (oder im Falle einer Wide-Character-Konstante im erweiterten Zeichensatz) nicht enthalten ist.

Wide-Characters werden unterstützt.

3.1.3.4 Aktuelle Locale-Einstellung, mit der Multibyte-Zeichen für eine Wide-Character-Konstante in die ensprechenden Wide-Characters konvertiert werden.

Wide-Character-Konstanten werden erkannt.

3.1.3.4 Wert einer Integer-Konstanten, die mehrere Zeichen enthält oder einer Wide-Character-Konstanten, die mehrere Multibyte-Zeichen enthält.

Zeichenkonstanten können ein oder zwei Zeichen enthalten. Bei zwei Zeichen nimmt das erste das höherwertige Byte und das zweite das niederwertige Byte ein.

3.2.1.2 Ergebnis der Konvertierung einer Ganzzahl in eine vorzeichenbehaftete Ganzzahl mit weniger Stellen, oder Ergebnis der Konvertierung einer Ganzzahl ohne Vorzeichen in eine Ganzzahl mit Vorzeichen gleicher Länge, wenn der Wert nicht repräsentiert werden kann.

Im Zuge dieser Konvertierungen werden die höherwertigen Bits einfach abgeschnitten. Vorzeichenbehaftete Ganzzahlen werden als Werte im 2x-Komplement gespeichert. Das Ergebnis wird somit als dieser Wert interpretiert. Wenn das höherwertige Bit der kleineren Ganzzahl nicht Null ist, wird der Wert als negativer Wert interpretiert, andernfalls ist er positiv.

3.2.1.3 Richtung beim Abschneiden, wenn eine Integralzahl in eine Gleitkommazahl konvertiert wird, die den ursprünglichen Wert nicht genau wiedergeben kann.

Der Wert der Ganzzahl wird auf den nächsten repräsentierbaren Wert gerundet. Der Wert des Typs long (231 –1) beispielsweise wird in den Wert des Typs float 231 konvertiert. Die Bindungen werden gemäß der vom IEEE festgelegten Standard-Arithmetik aufgelöst.

3.2.1.4 Richtung beim Abschneiden oder Runden, wenn eine Gleitzkommazahl in eine Gleitkommazahl mit weniger Stellen konvertiert wird.

Der Wert wird zum nächstliegenden darstellbaren Wert gerundet. Bindungen werden gemäß der vom IEEE festgelegten Standard-Arithmetik aufgelöst.

3.3 Ergebnisse bitweiser Operationen auf Ganzzahlen mit Vorzeichen

Die bitweisen Operatoren wirken sich auf vorzeichenbehaftete ganzzahlige Werte so aus, als würde es sich um die entsprechenden vorzeichenlosen Datentypen handeln. Das Vorzeichenbit wird als normales Datenbit verarbeitet. Das Ergebnis wird dann als vorzeichenbehaftete Ganzzahl im 2x-Komplement interpretiert.

3.3.2.3 Was passiert, wenn auf ein Element eines Variantenobjekts mit einem Element eines anderen Typs zugegriffen wird?

Der Zugriff wird zugelassen und das Element des anderen Typs greift auf die gespeicherten Bits zu. Sie müssen genau verstehen, wie die Bits von Gleitkommawerten verschlüsselt sind. Erst dann können Sie nachvollziehen, wie mit einem Element anderen Typs auf ein Gleitkommaelement zugegriffen wird. Wenn das gespeicherte Element weniger Stellen besitzt als das Element, mit dem auf den Wert zugegriffen wird, nehmen die überzähligen Bits ihren ursprünglichen Wert vor der Speicherung des Elements vom Typ Short an.

3.3.3.4 Welcher Ganzzahlentyp nimmt die maximale Größe eines Arrays auf?

Für ein normales Arrays ist der Typ signed int vorgesehen und für sehr große Arrays der Typ signed long.

3.3.4 Ergebnis des Umwandelns eines Zeigers in eine Ganzzahl oder umgekehrt.

Wenn Sie zwischen ganzen Zahlen und Zeigern derselben Größe konvertieren, werden keine Bits geändert. Wenn Sie von einem größeren Typ in einen kleineren Typ konvertieren, werden die höherwertigen Bit abgeschnitten. Bei Konvertierung einer kürzeren Ganzzahl in einen längeren Zeigertyp, wird die Ganzzahl zunächst auf einen Wert des Typs Integer erweitert, dessen Länge der des Zeigers entspricht.

Vorzeichenbehaftete Ganzzahlen werden also vorzeichenerweitert, um die zusätzlichen Bytes aufzufüllen. In ähnlicher Weise werden kleinere Zeigertypen, die auf größere Integer-Typ konvertiert werden sollen, zunächst auf einen Zeigertyp erweitert, der so lang ist wie der Integer-Typ.

3.3.5 Vorzeichen des Rests bei der Division von Ganzzahlen.

Das Vorzeichen für den Rest ist negativ, sobald einer der Operanden negativ ist. Wenn keiner oder beide Operanden negativ sind, wird der Rest positiv.

3.3.6 Welcher Integer-Typ kann den Unterschied zwischen zwei Zeigern zu Elementen desselben Arrays, ptrdiff_t, aufnehmen.

Der Integer nimmt den Typ signed int an, wenn die Zeiger vom Typ near sind (oder das Programm eine 32-Bit-Anwendung ist) oder signed long, wenn die Zeiger vom Typ far oder huge sind. Der Typ von ptrdiff_t hängt vom verwendeten Speichermodell ab. In kleinen Datenmodellen ist der Typ int, in großen Datenmodellen hat er den Typ long.

3.3.7 Ergebnis der Rechtsschiebung eines Integrals mit negativem Vorzeichen

Ein Wert mit negativem Vorzeichen wird bei der Verschiebung nach rechts vorzeichenerweitert.

3.5.1 In welchem Ausmaß können Objekte mit dem Speicherklassenspezifizierer register in Registern abgelegt werden.

Objekte, die mit beliebigen ein-, zwei-, oder vier-Byte-Integer- oder Zeigertypen von zwei Bytes deklariert werden, können in Registern abgelegt werden. Mindestens zwei und höchstens sieben Register stehen zur Verfügung. Die Zahl der tatsächlich von verwendeten Register, hängt davon ab, welche Register für die temporären Werte in der Funktion benötigt werden.

3.5.2.1 Wird ein einfaches Bitfeld  als Bitfeld des Typs signed int oder des Typs unsigned int behandelt?

Einfache Bitfelder des Typs int werden wie Bitfelder des Typs signed int behandelt.

3.5.2.1 Reihenfolge der Zuordnung von Bitfeldern innerhalb von Ganzzahlen.

Die Zuordnung der Bitfelder beginnt am niederwertigen Bit und wird in Richtung höherwertiges Bit fortgeführt.

3.5.2.1 Auffüllen und Ausrichten von Strukturelementen.

In der Voreinstellung ist das Auffüllen von Strukturen nicht vorgesehen. Wenn Sie die Option wortweise Ausrichtung (–a) verwenden, werden Strukturen auf gerade Größe aufgefüllt und alle Elemente, die nicht vom Typ Zeichen oder Zeichen-Array sind, werden zu einem geraden Mehrfach-Offset ausgerichtet.

3.5.2.1 Kann ein Bitfeld die Grenzen der Speichereinheit überschreiten?

Wenn keine Ausrichtung (–a) erforderlich ist, können Bitfelder dword-Grenzen überschreiten. Sie werden aber niemals in mehr als vier aneinandergrenzenden Bytes gespeichert.

3.5.2.2 Welcher Integer-Typ soll die Werte eines Aufzählungstyps repräsentierten?

Alle Aufzählungen werden als vollständige ints gespeichert. Alle Aufzählungen werden als long oder unsigned long gespeichert, wenn die Werte nicht in ein int passen. Dieses Verhalten ist durch die Compiler-Option –b voreingestellt.

Die Option –b- legt fest, daß Aufzählungen in den kleinstmöglichen Integertypen, die die Werte noch repräsentieren können, gespeichert werden sollen. Dazu gehören alle integralen Typen wie z.B. signed char, unsigned char, signed short, unsigned short, signed int, unsigned int, signed long und unsigned long.

Die Option –b- muß aus Kompatibiltätsgründen mit C++ festgelegt werden, da es nicht richtig ist, alle Aufzählungen als ints für C++ zu speichern.

3.5.3 Zugriffe auf Objekte des Typs als volatile qualifiziert ist.

Alle Verweise auf ein flüchtiges Objekt greifen auf das Objekt zu. Ob durch den Zugriff auf benachbarte Speicherstellen ebenfalls auf ein Objekt zugegriffen werden kann, hängt vom Aufbau des Speichers in der Hardware ab. Bei Speichern von speziellen Geräten, wie z.B. dem Speicher von Videoanzeigen, hängt es vom Aufbau des Geräts ab. Bei normalen PC-Speichern werden flüchtige Objekte nur für Speicher verwendet, die von asynchronen Interrupts adressiert werden. Der Zugriff auf benachbarte Objekte hat daher keine Auswirkung.

3.5.4 Maximale Zahl an Deklaratoren, die die Typen arithmetic, structure, oder union modifizieren können.

Die Zahl der Deklaratoren ist nicht eingeschränkt. Relativ viele Deklaratoren dürfen verwendet werden, die Zahl wird aber verringert, wenn sie tief in einer Gruppe von Blöcken in einer Funktion verschachtelt sind. Die zulässige Zahl auf Dateiebene beträgt mindestens 50.

3.6.4.2 Maximale Zahl von Case-Werten in einer switch-Anweisung

Die Zahl der Cases pro switch ist nicht genau beschränkt. Bei ausreichendem Arbeitsspeicher wird der Compiler die Case-Informationen akzeptieren.

3.8.1 Entspricht der Wert einer aus einem Zeichen bestehenden in einem Konstantenausdruck, der die bedingte Inklusion steuert, dem Wert derselben Zeichenkonstanten im Ausführungszeichensatz? Kann solch eine Zeichenkonstante einen negativen Wert annehmen?

Alle Zeichen-Konstanten, auch Konstanten in Bedingungsanweisungen, verwenden denselben Zeichensatz (Ausführung). Zeichenkonstanten aus einem Zeichen sind negativ, wenn der Typ des Zeichens ein Vorzeichen hat (Standard und –K nicht erforderlich).

3.8.2 Verfahren zum Feststellen der Position von einfügbaren Quelldateien.

Wenn die Include-Dateinamen in Include-Verzeichnissen an der Kommandozeile in spitzen Klammern angegeben sind, werden die Dateien in allen Include-Verzeichnissen gesucht. Dabei wird die folgende Reihenfolge eingehalten: zuerst in den an der Kommandozeile angegebenen Using-Verzeichnissen, dann in den in TURBOC.CFG oder BCC32.CFG angegebenen Using-Verzeichnissen. Wenn keine Include-Verzeichnisse angegeben sind, wird nur das aktuelle Verzeichnis nach den Dateien durchsucht.

3.8.2 Unterstützung von Namen in Anführungszeichen für einfügbare Quelldateien.

In Anführungszeichen eingebettete Dateinamen werden zunächst im aktuellen Verzeichnis gesucht. Falls der Suchvorgang erfolglos bleibt, wird die Suche derart vorgenommen, als wenn spitze Klammern angegeben wären.

3.8.2 Zuordnung von Zeichenfolgen bei Quelldateinamen.

Backslashes werden in Namen von Include-Dateien als getrennte Zeichen und nicht als Escape-Zeichen behandelt. Unterschiede bei der Groß- und Kleinschreibung werden bei Buchstaben ignoriert.

3.8.8 Definitionen für _ _DATE_ _ und _ _TIME_ _, wenn sie nicht verfügbar sind.

Datum und Uhrzeit sind immer verfügbar. Sie richten sich nach Datum und Uhrzeit des Betriebssystems.

4.1.1 Dezimaltrennzeichen.

Als Dezimaltrennzeichen wird ein Punkt (.) verwendet.

4.1.5 Typ des sizeof-Operators, size_t.

Der Datentyp size_t ist unsigned.

4.1.5 Auf welche Nullzeigerkonstante wird das Makro NULL erweitert?

Bei einer 16-Bit-Anwendung auf ein Integer oder Long 0, je nach Speichermodell.

Bei 32-Bit-Anwendungen wird NULL zu einer Null des Typs int oder long erweitert. Beides sind 32-Bit-Zahlen mit Vorzeichen.

4.2 Von der Funktion Assert ausgegebene Diagnosemeldung und Abschlußverhalten.

Die ausgegebene Diagnosemeldung lautet: "Assertion failed: expression, file filename, line nn", wobei expression für den ausgeführten Ausdruck steht, der fehlgeschlagen ist, filename für den Quelldateinamen und nn für die Nummer der Zeile, in der die Ausführung stattfand.

Abort wird sofort nach Einblenden der Ausführungsmeldung aufgerufen.

4.3 Implementationsspezifische Aspekte der Zeichenprüfung und Funktionen zur Case-Zuordnung.

Unter 4.3.1 angegeben.

4.3.1 Von den Funktionen isalnum, isalpha, iscntrl, islower, isprint und isupper geprüfte Zeichensätze.

Zuerst 128 ASCII-Zeichen für die voreingestellte C Locale-Einstellung. Ansonsten alle 256 Zeichen.

4.5.1 Von den mathematischen Funktion bei Domain-Fehlern zurückgegebene Funktionen

Ein IEEE-NAN (keine Zahl).

4.5.1 Setzen die mathematischen Funktionen bei Fehlern des Underflow-Bereichs den ganzzahligen Ausdruck errno auf den Wert des Makros ERANGE?

Nein, dies gilt nur für die anderen Fehler - Domain, Singularität, Überlauf und vollständiger Verlust der Genauigkeit.

4.5.6.4 Tritt ein Domain-Fehler auf oder wird eine Null zurückgegeben, wenn die Funktion fmod ein zweites Null-Argument aufweist?

Die Funktion fmod(x,0) gibt 0 zurück.

4.7.1.1 Menge der Signale für die Signal-Funktion.

SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV und SIGTERM.

4.7.1.1 Semantik der einzelnen von der Signalfunktion erkannten Signale.

Siehe die Erläuterungen unter signal.

4.7.1.1 Die Standardverarbeitung sowie die Verarbeitung bei Programmstart der einzelnen von der Signalfunktion erkannten Signale.

Siehe die Erläuterungen unter signal.

4.7.1.1 Die Blockierung des ausgeführten Signals, wenn die Ensprechung von Signal (sig, SIG_DFL) nicht vor dem Aufruf des Signal-Handlers ausgeführt wird.

Die Entsprechung von signal(sig, SIG_DFL) wird immer ausgeführt.

4.7.1.1 Wird die Standard-Verarbeitung zurückgesetzt, wenn das Signal SIGILL von einer für die Signalfunktion festgelegten Behandlungsroutine empfangen wird?

Nein, es wird nicht zurückgesetzt.

4.9.2 Muß an das Ende der letzten Zeile eines Text-Streams ein Zeilenvorschub gesetzt werden?

Nein, es ist kein Zeilenvorschub erforderlich.

4.9.2 Werden Leerzeichen, die direkt vor einem Zeilenvorschub eingegeben werden, beim Einlesen angezeigt?

Ja, sie werden angezeigt.

4.9.2 Wieviele Nullzeichen dürfen an die in einen binären Stream geschriebenen Daten angehängt werden?

Keine.

4.9.3 Wird der Indikator für die Dateiposition eines Streams im Append-Modus anfänglich an den Anfang oder das Ende einer Datei gesetzt.

Der Indikator der Dateiposition eines Streams im Append-Modus wird am Anfang der Datei initialisiert. Vor jedem Schreibzugriff wird er an das Ende der Datei zurückgesetzt.

4.9.3 Wird bei einem Schreibzugriff auf ein Text-Stream die zugehörige Datei ab dieser Stelle abgeschnitten?

Ein Schreibzugriff von 0 Byte kann, muß die Datei aber nicht abschneiden. Dies hängt davon ab, wie die Datei zwischengespeichert ist. Am sichersten ist es, einen Schreibzugriff einer Länge von 0 Byte mit unbestimmten Verhalten zu definieren.

4.9.3 Eigenschaften der Dateipufferung.

Dateien können vollständig, zeilenweise oder gar nicht zwischengespeichert werden. Wenn eine Datei zwischengespeichert ist, wird beim Öffnen der Datei ein Standardpuffer von 512 Byte erstellt.

4.9.3 Gibt es Dateien mit einer Größe von Null?

Ja, die gibt es.

4.9.3 Kann dieselbe Datei mehrere Male geöffnet werden?

Ja, das ist möglich.

4.9.4.1 Auswirkung der Funktion remove auf eine geöffnete Datei.

Keine spezielle Überprüfung nach einer bereits geöffneten Datei wird durchgeführt. Der Programmierer trägt die Verantwortung für diesen Schritt.

4.9.4.2 Eine Datei unter dem neuen Namen existiert bereits vor der Umbenennung. Was passiert?

Rename gibt –1 zurück und errno ist auf EEXIST eingestellt.

4.9.6.1 Ausgabe für %p-Konvertierung in fprintf.

In Near-Datenmodellen vier Hexadezimalzahlen (XXXX). In Far-Datenmodellen vier Hexadezimalzahlen, Doppelpunkt, vier Hexadezimalzahlen (XXXX:XXXX). (Bei 16-Bit-Programmen.)

Acht Hexadezimalzahlen (XXXXXXXX). (Bei 32-Bit-Programmen.)

4.9.6.2 Eingabe für %p-Konvertierung in fscanf.

Siehe 4.9.6.1.

4.9.6.2 Wie wird das Zeichen '–' (Bindestrich)  interpretiert, das in der Scanliste für eine %[ -Konvertierung in fscanf weder das erste noch das letzte Zeichen ist?

Siehe die Beschreibung von scanf.

4.9.9.1 Auf welchen Wert wird das Makros errno von den Funktionen fgetpos or ftell gesetzt, wenn ein Fehler auftritt?

EBADF Schlechte Dateinummer.

4.9.10.4 Die von perror erzeugten Meldungen.

Meldungen, die sowohl in Win16 als auch Win32 erzeugt werden

Arg list too big (Argumentliste zu lang)	Math argument (Mathematisches Argument)

Attempted to remove current directory	Memory arena trashed
(Versuch, das aktuelle Verzeichns zu löschen)	(Speicherbereich nicht mehr existent)

Bad address (Falsche Adresse)	Name too long (Name zu lang)

Bad file number (Falsche Dateinummer)	No child processes (Keine
untergeordnete Prozesse)

Block device required (Blockgerät erforderlich)	No more files (Keine weiteren Dateien)

Broken pipe (Fehlerhafte Pipe)	No space left on device (Kein
Speicherplatz auf dem Gerät verfügbar)

Cross-device link (Kreuzverbindung von Geräten)	No such device (Gerät existiert nicht)

Error 0 (Fehler 0) 	No such device or address (Gerät oder Adresse existiert nicht)

Exec format error (Fehler beim Exec-Format)	No such file or directory (Datei
oder Verzeichnis existiert nicht)

Executable file in use	No such process (Prozess existiert nicht)
(Ausführbare Datei wird verwendet)

File already exists (Datei bereits vorhanden)	Not a directory (Kein Verzeichnis)

File too large (Datei zu groß)	Not enough memory (Speicher nicht 	ausreichend)

Illegal seek (Unzulässige Positionierung)	Not same device (Nicht dasselbe Gerät)

Inappropriate I/O control operation	Operation not permitted (Operation nicht (Unzulässiger E/A-Steuervorgang)	zulässig)

Input/output error (Fehler bei Ein-/Ausgabe)	Path not found (Pfad nicht gefunden)

Interrupted function call (Funktionsaufruf gestört)	Permission denied ( verweigert)

Invalid access code (Ungültiger Zugriffscode)	Possible deadlock (Mögliche Sperre)

Invalid argument (Ungültiges Argument)	Read-only file system
(Schreibgeschütztes Dateisystem)

Invalid data (Ungültige Daten)	Resource busy (Ressource belegt)

Invalid environment (Ungültige Umgebung)	Resource temporarily unavailable
(Ressource vorübergehend nicht verfügbar)

Invalid format (Ungültiges Format)	Result too large (Ergebnis zu groß)

Invalid function number	Too many links (Zu viele Verknüpfungen)
(Ungültige Funktionsnummer)

Invalid memory block address	Too many open files (Zu viele Dateien
(Ungültige Speicherblockadresse)	geöffnet)

Is a directory (Ist ein Verzeichnis)

Meldungen, die nur in Win32 erzeugt werden

Bad address (Falsche Adresse)	No child processes (Keine Kindprozesse)

Block device required (Blockgerät erforderlich)	No space left on device (Kein Speicherplatz auf
dem Gerät)

Broken pipe (Fehlerhafte Pipe)	No such device or address (Gerät oder Adresse
existiert nicht)

Executable file in use (Ausführbare Datei	No such process (Prozeß existiert nicht)
wird verwendet)

File too large (Datei zu groß)	Not a directory (Kein Verzeichnis)

Illegal seek (Unzulässige Positionierung)	Operation not permitted (Vorgang nicht
zulässig)

Inappropriate I/O control operation	Possible deadlock (Mögliche Sperre)
(Unzulässiger E/A-Steuervorgang)

Input/output error (Fehler bei Ein-/Ausgabe)	Read-only file system (Schreibgeschütztes
Dateisystem)

Interrupted function call (Funktionsaufruf	Resource busy (Ressource belegt)
unterbrochen)

Is a directory (Ist ein Verzeichnis)	Resource temporarily unavailable (Ressource
vorübergehend nicht verfügbar)

Name too long (Name zu lang)	Too many links (Zu viele Verknüpfungen)

4.10.3 Das Verhalten von calloc, malloc oder realloc bei erforderlicher Größe 0.

Die Funktionen calloc und malloc ignorieren die Anforderung und geben O zurück. realloc wird den Block frei machen.

4.10.4.1 Das Verhalten der Funktion abort bezüglich geöffneter und temporärer Dateien.

Die Dateipuffer werden nicht geleert und die Dateien nicht geschlossen.

4.10.4.3 Welcher Status wird von exit zurückgegeben, wenn der Wert des Arguments nicht Null, EXIT_SUCCESS oder EXIT_FAILURE ist?

Kein außergewöhnlicher. Der Status wird genauso zurückgegeben wie er übergeben wurde. Der Status wird durch ein signed char wiedergegeben.

4.10.4.4 Einrichten der Umgebungsnamen und Verfahren für das Ändern der von getenv verwendeten Umgebungsliste.

Die Umgebungs-Strings diejenigen, die im Betriebssystem mit dem Befehl SET definiert wurden. Mit putenv können die Strings für die Dauer des aktuellen Programms geändert werden. Mit dem Befehl SET werden Umgebungs-Strings dauerhaft geändert.

4.10.4.5 Inhalt und Modus der Ausführung des Strings durch die Systemfunktion

Der String wird als Befehl des Betriebssystems interpretiert. Entweder wird COMSPEC verwendet, COMMAND.COM (bei 16-Bit-Programmen) oder CMD.EXE (bei 32-Bit-Programmen) ausgeführt. Der Argumenten-String wird als auszuführender Befehl übergeben. Alle im Betriebssystem integrierten Befehle sowie Stapeldateien und ausführbare Programme können ausgeführt werden.

4.11.6.2 Inhalt der von strerror zurückgegebenen Fehlermeldungs-Strings.

Siehe 4.9.10.4.

4.12.1 Lokale Zeitzone und Sommerzeit.

Als lokale Uhrzeit und Datum des PC definiert.

4.12.2.1 Die Zeitrechnung der Uhr.

Wird in Timer-Ticks wiedergegeben, beginnend mit dem Anfang der Programmausführung.

4.12.3.5 Formate für Datum und Uhrzeit.

 C++Builder implementiert ANSI-Formate.


Schlüsselwörter (alphabetisch)
Schlüsselwörter

Schlüsselwörter sind für spezielle Zwecke reservierte Wörter, die nicht als normale Bezeichner (Namen) verwendet werden dürfen.

Die folgende Tabelle enthält alle in dieser Version von C++Builder gültigen Schlüsselwörter in alphabetischer Reihenfolge.

Bei einigen Schlüsselwörter steht die Beschreibung nur unter dem dazugehörigen Thema zur Verfügung. Beispielsweise werden die C++ Umwandlungsoperatoren in Neue Typumwandlung besprochen und die Schlüsselwörter zu C++ Namensbereichen finden Sie in Namensbereiche: Überblick .

Eine nach Funktion geordnete Aufstellung der Schlüsselwörter finden Sie unter Schlüsselwörter (nach Kategorien).

A

__asm

_asm

asm

auto

__automated

B

break

bool

C

case

catch

__cdecl

_cdecl

cdecl

char

class

__classid

__closure

const

const_cast

continue

D

__declspec

default

delete

__dispid

do

double

dynamic_cast

E

else

enum

__except

explicit

_export

__export

extern

F

false

__fastcall

_fastcall

__finally

float

for

friend

G

goto

I

if

__import

_import

inline

int

__int8

__int16

__int32

__int64

L

long

mutable

N

namespace

new

O

operator

P

__pascal

_pascal

pascal

private

protected

__property

public

__published

R

register

reinterpret_cast

return

__rtti

S

short

signed

sizeof

static

static_cast

__stdcall

_stdcall

struct

switch

T

template

this

__thread

throw

true

__try

try

typedef

typename

typeid

U

union

using

unsigned

V

virtual

void

volatile

W

wchar_t

while


Schlüsselwörter (nach Kategorie)
Schlüsselwörter (nach Kategorien)

Die folgende Aufstellung zeigt, in welche Kategorien die von C++Builder unterstützten Schlüsselwörter eingeteilt werden können. Eine alphabetisch geordnete Aufstellung der Schlüsselwörter finden Sie unter Schlüsselwörter (alphabetisch).

Zusätzlich bei C++Builder 	Schlüsselwörter nur bei C++Builder

C++ spezifisch	Schlüsselwörter, die nur in C++ erkannt werden.

Modifizierer	Schlüsselwörter, die ein oder mehrere Attribute eines sich auf ein Objekt beziehenden Namens verändern.

Operatoren	Schlüsselwörter, die Funktionen aufrufen, die auf Objekte oder Bezeichner wirken.

Anweisungen	Schlüsselwörter, die den Programmablauf während der Ausführung steuern.

Speicherklassen-Spezifizierer	Schlüsselwörter, die den Gültigkeitsbereich und die Lebensdauer für einen Bezeichner bestimmen.

Typ-Spezifizierer	Schlüsselwörter, die bestimmen, wieviel Speicher reserviert und wie die gespeicherten Bitmuster interpretiert werden.


Modifizierer

Modifizierer können in Deklarationen eingesetzt werden, um die Speicherbelegung und Zugriffsart für Bezeichner/Objekte zu beeinflussen.

Bei C++Builder gibt es folgende Modifizierer:

__cdecl

const

__declspec

__dispid

__export

__fastcall

__import

__pascal

__rtti

__stdcall

volatile


Operator Schlüsselwörter
Operator-Schlüsselwörter

Siehe auch

Einige Schlüsselwörter von C++Builder bezeichnen Operatoren, die Funktionen aufrufen, die auf Objekte und Bezeichner wirken.

Bei C++Builder gibt es folgende Operator-Schlüsselwörter:

delete

operator

typeid

new

sizeof

__classid


Anweisungen: Schlüsselwörter
Anweisungsschlüsselwörter

Anweisungen steuern den Ablauf während der Programmausführung. Enthält ein Programm keine speziellen Sprung- und Auswahlanweisungen, so werden die Anweisungen nacheinander in der Reihenfolge ausgeführt, in der sie im Quelltext stehen.

Bei C++Builder gibt es folgende Anweisungsschlüsselwörter:

break	else	switch

case	__finally	throw

catch	for	__try

continue	goto	try

default	if	while

do	return

__except


asm, _asm und __asm (Schlüsselwörter)
asm, _asm,  _ _asm

Siehe auch	Beispiel	Schlüsselwörter

Syntax

asm <opcode> <operanden>  <; oder neue-zeile>

_asm <opcode> <operands>  <; oder neue-zeile>

__asm <opcode> <operands> <; oder neue-zeile>

Beschreibung

Die Schlüsselwörter asm, _asm und __asm ermöglichen es Ihnen, Assemblerbefehle direkt in Ihren C- oder C++ Quelltext einzufügen. Jedes C++ Symbol wird durch das entsprechende Assembleräquivalent ersetzt.

Um mehrere Anweisungen in Assemblersprache zu einem Block zusammenzufassen, beginnen Sie diesen mit dem Schlüsselwort _asm, und schließen Sie die darauf folgenden Assembler-Anweisungen in geschweifte Klammern {} ein.


Beispiele

// Einfügen einer einzelnen Assembleranweisung in das Programm:

asm pop dx

// Wenn Sie mehrere Assembler-Anweisungen einfügen wollen,

// müssen Sie sie in geschweifte Klammern einschließen:

asm {

  mov ax, 0x0e07

  xor bx, bx

  int 0x10         // Erzeugt einen Signalton im Systemlautsprecher

}


auto (Schlüsselwort)
auto

Beispiel	Schlüsselwörter

Syntax

[auto] <datendefinition> ;

Beschreibung

Der Speicherklassen-Spezifizierer auto dient dazu, eine lokale Variable mit lokaler Lebensdauer zu definieren.

Diese Speicherklasse ist für lokale Variablen voreingestellt und wird deshalb selten explizit angegeben.


Beispiel

int main()

{

  auto int i;

  i = 5;

  return i;

}


__automated

Siehe auch	Schlüsselwörter	Beispiel

Syntax

_automated: <deklarationen>

Beschreibung

Die Regeln für die Sichtbarkeit von automated-Elementen sind identisch mit denen der public-Elemente. Der einzige Unterschied zwischen automated- und public-Elementen ist, daß OLE-Automatisierungsinformation für Elementfunktionen und Eigenschaften generiert wird, die in automated-Abschnitten deklariert werden. Diese OLE-Automatisierungs-Typinformation macht es möglich, OLE-Automatisierungsserver zu erzeugen.

Für eine Elementfunktion müssen die Typen aller Parameter der Elementfunktion und alle Funktionsergebnisse (wenn vorhanden) automatisierbar sein. Ähnlich müssen für eine Eigenschaft der Eigenschaftstyp und die Typen einer Array-Eigenschaftsparameter automatisierbar sein. Die automatisierbaren Tpyne sind: Currency, double, int, float, short, String, TDateTime, Variant und unsigned short. Die Deklaration von Elementfunktionen oder Eigenschaften in __automated-Abschnitten, die nicht-automatisierbar sind, für zu einen Compilierzeitfehler.

Deklarationen von Elementfunktionen müssen die Aufrufkonvention __fastcall verwenden.

Elementfunktionen können virtual sein.

Elementfunktionen können __dispid(konstanter int ausdruck) nach der schließenden Klammer der Parameterliste hinzufügen.

Eigenschaftsdeklarationen können nur Zugriffsspezifizierer  (__dispid, read, and write) enthalten. Andere Spezifizierer (index, stored, default, nodefault) sind nicht erlaubt.

Eigenschafts-Zugriffsspezifizierer müssen einen Elementfunktionsbezeichner enthalten. Datenelement-Bezeichner sind nicht erlaubt.

Eigenschaftzugriffs-Elementfunktionen müssen die Aufrufkonvention __fastcall verwenden.

Eigenschaftsdeklarationen die den Eigenschaftstyp nicht enthalten, sind nicht erlaubt.


automated und dispid
Beispiel für __automated und __dispid

// Veranschaulicht die Verwendung von __automated und __dispid.

class myclass : TAutoObject

{

// Dieser Zugriffsabschnitt wird für die Deklaration von Funktionen und

// Eigenschaften verwendet, die OLE-Automatisierungsinformationen benötigen.

__automated :

   // Die Direktive __dispid verbindet die OLE-Automatisierungs-

   // Verteiler-ID 1000 mit der Elementfunktion.

   void __fastcall func(void)  __dispid(1000);

};


bool

Siehe auch	Beispiel	Schlüsselwörter

Syntax

bool <identifier>;

Beschreibung

Mit bool und den literalen Konstanten false und true läßt sich nach den Regeln der Booleschen Algebra rechnen.

Das Schlüsselwort bool kennzeichnet einen Datentyp, der nur die Werte false oder true aufnehmen kann. DieSchlüsselwörter false und true sind Boolesche Konstanten mit vordefinierten Werten. Das numerische Äquivalent für false ist Null und true entspricht der Eins. Diese Booleschen Konstanten sind R-Werte, es lassen sich ihnen also keine Werte zuweisen.

Sie können allerdings eine R-Wert vom Typ bool in einen R-Wert vom Typ int umwandeln. Die numerische Umwandlung setzt false auf den Wert Null und true auf den Wert Eins.

Umgekehrt ist es möglich, R-Werte verschiedener Art (arithmetisch, Aufzählung, Zeiger, Elementzeiger) in einen R-Wert vom Typ bool zu konvertieren. Dabei wird eine arithmetische Null bzw. ein Zeigerwert NULL in den Wert false, und jeder andere Wert zu true umgewandelt.


Beispiel

/* Boolesche Tests mit bool, true, und false. */

#include <iostream.h>

bool func() {     // Funktion mit Resultat vom Typ bool

   return NULL;   // NULL wird in ein Boolesches false umgewandelt

// return false;  // Gleichwertig zur vorherigen Anweisung

   }

int main() {

   bool val = false;  // Boolesche Variable

   int i = 1;         // i ist weder Boolesch true noch Boolesch false

   int *iptr = 0;     // NULL-Zeiger

   float j = 1.01;    // j ist weder Boolesch true noch Boolesch false

   // Integer-Tests

   if (i == true)  cout << "True: Wert ist 1." << endl;

   if (i == false) cout << "False: Wert ist 0." << endl;

   // Test mit Zeigern

   if (iptr == false) cout << "Ungültiger Zeiger." << endl;

  // Um für j den Wahrheitswert zu testen, sollte eine explizite
// Typumwandlung (cast) erfolgen.

  if (bool(j) == true) cout << "Boolescher Wert von j ist True." << endl;

   // Test auf den Boolesche Funktionswert

   val = func();

   if (val == false)

      cout << "func() liefert false.";

   if (val == true)

      cout << "func() liefert true.";

   return false;  // false wird hier zu 0 konvertiert

}

Das Programm liefert folgende Ausgabe:

True: Wert ist 1

g enthält unbekannten Wahrheitswert.

Ungültiger Zeiger.

Boolescher Wert von j ist True.

func() liefert false.


break

Siehe auch	Beispiel	Schlüsselwörter

Syntax

break ;

Beschreibung

Die Anweisung break bewirkt innerhalb einer Schleife, daß die Ablaufkontrolle zur ersten Anweisung hinter der innersten Schleife, in der break steht, springt.


Beispiel

/* Veranschaulicht die Verwendung der Schlüsselwörter  break, case, default, und switch. */

#include <conio.h>

#include <stdio.h>

int main(void) {

   int ch;

   printf("\tPRESS a, b, OR c. ANY OTHER CHOICE WILL "

          "TERMINATE THIS PROGRAM.");

   for ( /* FOREVER */; ((ch = getch()) != EOF); )

      switch (ch) {

         case 'a' :    /* DIE AUSWAHL VON a HAT EINEN EIGENEN ABLAUF. */

            printf("\nOption a was selected.\n");

            break;

         case 'b' :    /* b UND c ERHALTEN GLEICHE ERGEBNISSE. */

         case 'c' :

            printf("\nOption b or c was selected.\n");

            break;

         default :

            printf("\nNOT A VALID CHOICE!  Bye ...");

            return(-1);

         }

   return(0);

   }


case (Schlüsselwort)
case

Siehe auch	Beispiel	Schlüsselwörter

Syntax

switch ( <switch_variable> ){

  case <konstantenausdruck> : <anweisung>; [break;]

    .

    .

    .

  default : <anweisung>;

}

Beschreibung

Die Anweisung case bestimmt zusammen mit switch, welche Anweisungen ausgeführt werden.

Die Liste der möglichen Verzweigungspunkte wird durch Konstrukte der folgenden Form gebildet:

case <konstantenausdruck> : <anweisung>;

Dabei muß <konstantenausdruck> vom Typ int sein und einen innerhalb der switch-Anweisung nicht mehrfach vorkommenden Wert haben.

Die Werte von <konstantenausdruck> werden auf Übereinstimmung mit <switch_variable> geprüft.

Wenn eine Übereinstimmung gefunden wird, so wird der Programmablauf mit den der betreffenden case-Anweisung folgenden Anweisungen fortgesetzt, bis eine break-Anweisung oder das Ende der switch-Anweisung erreicht wird.

Wenn keine Übereinstimmung gefunden wird, so wird der Ablauf hinter der default-Anweisung fortgesetzt.

Hinweis:	Es ist unzulässig, daß case-Konstanten in derselben switch-Anweisung doppelt vorkommen.


catch

Siehe auch	Schlüsselwörter

Syntax

catch (exception_deklaration) <zusammengesetzte_anweisung>

Beschreibung

Das Schlüsselwort catch dient zur Kennzeichnung eines Exception-Handlers. Der Handler muß unmittelbar nach der durch das Schlüsselwort try markierten Anweisung stehen. Das Schlüsselwort catch kann auch unmittelbar hinter einer anderen catch-Anweisung stehen. Jeder Handler reagiert nur auf eine Exception, die mit dem in seiner Argumentliste spezifizierten Typ übereinstimmt oder in diesen konvertiert werden kann.


cdecl, _cdecl, und __cdecl
cdecl, _cdecl,  _ _cdecl

Beispiel	Schlüsselwörter

Syntax

cdecl <daten-/funktions-definition> ;

_cdecl <daten-/funktions-definition> ;

__cdecl <daten-/funktions-definition> ;

Beschreibung

Die Modifizierer cdecl, _cdecl und __cdecl werden benutzt, um eine Variable oder eine Funktion gemäß den C-Namenskonventionen (Unterscheidung von Groß-/Kleinschreibung, als erstes Zeichen wird ein Unterstrich hinzugefügt) zu deklarieren. Wenn Sie einem Funktionsnamen cdecl, _cdecl oder _ _cdecl voranstellen, so wird damit auch die Art der Parameterübergabe beeinflußt (Der letzte Parameter kommt zuerst auf den Stack, das aufrufende Programm bereinigt den Stack). Der _ _cdecl-Modifizierer überschreibt die Compiler-Direktiven und DIE-Optionen.

cdecl, _cdecl und __cdecl sind spezifische Schlüsselwörter von C++Builder.


Beispiel

int cdecl DateiZaehler;

far cdecl long MeineFunktion(int x);


char (Schlüsselwort)
char

Siehe auch	Schlüsselwörter

Syntax

[signed|unsigned] char <variablen-name>

Beschreibung

Dieser Typ-Spezifizierer dient zur Deklaration einer Zeichenvariablen; Variablen vom Typ char belegen 1 Byte an Speicherplatz.

Eine char-Variable kann signed, unsigned oder unspezifiziert sein.

Objekte, die als Typ char deklariert sind, können jedes Zeichen aus dem nicht erweiterten ASCII-Zeichensatz aufnehmen.


class (Schlüsselwort)
class

Siehe auch	Beispiel	Schlüsselwörter

Syntax

<klassenschlüssel> <klassenname> <basisliste> { <elementliste> }

<klassenschlüssel> ist eins der Schlüsselworte class, struct oder union

<klassenname> ist ein beliebiger Name, der innerhalb seines Gültigkeitsbereichs eindeutig ist.

<basisliste> ist eine Liste der Basisklasse(n), von der diese Klasse abgeleitet ist.Die Angabe von <basisliste> ist optional.

<elementliste> deklariert die Datenelemente und Elementfunktionen dieser Klasse.

Beschreibung

Das Schlüsselwort class wird für die Definition einer C++ Klasse verwendet.

Innerhalb einer Klasse werden

die Daten als Datenelemente bezeichnet,

die Funktionen als Elementfunktionen bezeichnet.


Beispiel

class sterne {

   int groesse;                // Datenelement

   int sternfunktion(void);    // Elementfunktion

};


__classid

Siehe auch	Schlüsselwörter	Beispiel

Syntax

__classid(classType)

Beschreibung

Der Operator __classid liefert einen Zeiger auf vtable des angegebenen Typs classType. Der Operator wird intern von den VCL-Unterstützungsklassen und dem erzeugten Projekt-Quelltext verwendet, um mit VCL-Methoden (auch Elementfunktionen genannt) zu interagieren, die Klassenparameter evrwenden.

Dieser Operator sollte nicht direkt von C++Builder-Programmierern verwendet werden.


__closure

Siehe auch	Beispiel	Schlüsselwörter

Syntax

<type> ( __closure * <id> ) (<param list>);

Beschreibung

Verwenden Sie __closure um Ereignisbehandlungsfunktionen zu deklarieren.

Eine closure-Deklaration ist das gleiche wie eine Funktionszeiger-Deklaration, jedoch mit dem Zusatz, daß das Schlüsselwort __closure vor dem <id> definiert wird. Während ein Funktionszeiger nur eine 4-Byte Codeadresse enthält, enthält ein closure sowohl Codeadresse (in den erste 4 Bytes) als auch einen Objektzeiger (in den nächsten 4 Bytes), der als this-Zeiger dient, wenn Sie über den closure aufrufen.


closure Beispiel

struct MyObject

{

   double MemFunc(int);

};

double func1(MyObject *o)

{

   // Ein Zeiger auf eine Elementfunktion

   // erwartet int-Argument und liefert double.

   double ( __closure *myClosure )(int);

   // Closure initialisieren.

   myClosure = o -> MemFunc;

   // Closure verwenden um Elementfunktion aufzurufen und int übergeben.

   return myClosure(1);

}


const (Schlüsselwort)
const

Siehe auch	Beispiel	Schlüsselwörter

Syntax

const <variablenname> [ = <wert> ] ;

<funktionsname> ( const <typ>*<variablenname> ;)

<funktionsname> const;

Beschreibung

Der Modifizierer const wird verwendet, um Änderungen des Werts einer Variable zu verhindern.

Verwenden Sie den Modifizierer const, um einer Variablen einen vom Programm nicht mehr veränderbaren Anfangswert zu geben. Jede spätere Wertzuweisung für eine const-Variable führt zu einer Fehlermeldung des Compilers.

Ein Zeiger const kann nicht verändert werden, obwohl das Objekt, das auf diesen Zeiger zeigt, verändert werden kann. Betrachten Sie dazu die folgenden Beispiele.

   const float pi = 3.14;

   const maxint = 12345;     // Falls Selbstbestimmend, entspricht const
                             // einem Wert vom Typ int.

   char *const str1 = "Hello, Welt";            // Ein konstanter Zeiger

   char const *str2 = "Borland International";  // Ein Zeiger auf einen
                                                // konstanten String.

Wenn diese Deklarationen vorgegeben sind, sind die folgenden Anweisungen ungültig.

pi   = 3.0;         // Weist einer Konstante einen Wert zu.

i     = maxint++;   // Inkrementiert eine Konstante.

str1 = "Hi, there!" // Laesst str1 auf irgendetwas zeigen.

Verwendung des Schlüsselworts const in C++ Programmen:

In C++ ist die Verwendbarkeit von const auf Klassen und Elementfunktionen erweitert. Wenn Sie in einer C++ Klassendefinition den Modifizierer const hinter die Deklaration einer Elementfunktion setzen, so kann diese Funktion keine Daten der Klasse verändern.

Ein Klassenobjekt, das mit dem Schlüsselwort const definiert worden ist, kann nur solche Elementfunktionen aufrufen, die ebenfalls mit const definiert worden sind. Der Versuch, eine nicht mit const definierte Elementfunktion aufzurufen, führt zu der Fehlermeldung non-constant function class-name::member-function-name() (Nicht-konstante Funktion Klassenname::Elementfunktionsname()) Durch diese Verwendungsmöglichkeit des Schlüsselworts const bietet C++ zusätzliche Sicherheit; der Compiler unterstützt diese Methode aber nicht automatisch.

Warnung:	Eine const-Variable kann indirekt über einen Zeiger verändert werden, wie in folgendem Beispiel:

*(int *)&mein_alter = 35;

Wird der Modifizierer const für einen Zeigerparameter in der Parameterliste einer Funktion benutzt, so kann die Funktion die Variable, auf die der Zeiger weist, nicht verändern. Beispiel dazu:

int printf (const char *format, ...);

Die Funktion printf kann nun den Formatstring nicht mehr verändern.


Beispiel 1

class X  {

   int j;

public:

  X::X() { j = 0; };

  int UntereGrenze() const;           // ÄNDERT KEINE DATENELEMENTE

  int dimension(X x1, const X &x2) {  // DIE MIT x2 REFERENZIERTEN PARAMETER WERDEN NICHT GEÄNDERT

     x1.j = 3;                        // DAS MIT x1 BEZEICHNETE OBJEKT IST ÄNDERBAR:

     x2.j = 5;                        // FEHLER! x2 IST NICHT ÄNDERBAR.

     return x2.j;

     }

};

Beispiel 2

#include <iostream.h>

class Alpha {

   int num;

public:

   Alpha(int j = 0) { num = j; }

   int func(int i) const {

      cout << "Nicht-veränderbare Funktion." << endl;

      return i++;

      }

   int func(int i) {

      cout << "Verändere private Daten" << endl;

      return num = i;

      }

   int f(int i) { cout << "Nicht-konstante Funktion mit i = " << I
                     << " aufrufen" << endl; return i;}

};

void main() {

   Alpha alpha_mod;         // Ruft die nicht-konstante Funktion auf.

   const Alpha alpha_inst;  // Versucht die konstante Funktion aufzurufen.

   alpha_mod.func(1);

   alpha_mod.f(1);          // Erwirkt eine Compiler-Warnung.

   alpha_inst.func(1);

   alpha_inst.f(1);

   }

Ausgabe:

Verändere private Daten

Nicht-konstante Funktion mit i = 1 aufrufen

Nicht-veränderbare Funktion.

Nicht-konstante Funktion mit i = 1 aufrufen


continue

Siehe auch	Beispiel	Schlüsselwörter

Syntax

continue ;

Beschreibung

Die Anweisung continue wird innerhalb von Schleifen benutzt, um die Steuerung an das Ende der innersten Schleife, in der continue steht, springen zu lassen. An diesem Punkt wird dann die Fortsetzungsbedingung der Schleife erneut geprüft.


Beispiel

void main ()

{

  for (i = 0; i < 20; i++) {

     if (array[i] == 0)

       continue;

     array[i] = 1/array[i];

  }

}


_ _declspec

Siehe auch	Beispiel	Schlüsselwörter

Syntax

__declspec(<decl-modifier>)

Beschreibung

Lesen Sie auch die Verwendung des Schlüsselworts __declspec beim portieren virtueller Funktionen aus Delphi-Quelltext.

Das Schlüsselwort _ _declspec wird bei der Erstellung von forward-Deklarationen aus VCL-Klassen Benötigt.

 _ _declspec dient der Kennzeichnung der für eine Variablen oder Funktion geeigneten Speicherklassenattribute

Das Schlüsselwort _ _declspec erweitert die Möglichkeiten der Verwendung von Speicherklassen-Modifizierern in Deklarationen. _ _declspec und seine Argumente können an beliebiger Stelle in der Deklaratorenliste erscheinen, - im Gegensatz zu den Modifizierern alten Typs, die jeweils unmittelbar von den zu modifizierenden Bezeichner zu stellen waren.

__export void f(void);                      // ungültig

void __export f(void)                       // korrekt

void __declspec(dllexport) f(void);         // korrekt

__declspec(dllexport)void f(void);          // korrekt

class __declspec(dllexport) ClassName { }   // korrekt

Zu decl-modifier passen nur folgende Argumente: dllexport, dllimport, oder thread. Die Bedeutung dieser Argumente entspricht den folgenden Schlüsselwörtern zur Bezeichnung von Speicherklassen.

Argument	Speicherklasse	Compiler-Unterstützung

dllexport	__export 	32- und 16-Bit

dllimport	__import 	32- Bit (zulässig, allerdings ohne Auswirkung auf 16-Bit Programme)

thread	__thread	nur 32- Bit


Beispiel

/* Beispiel für die Verwendung von __declspec-Deklarationen. */

__declspec(dllimport) void func(void);

__declspec(dllimport) int a;

__declspec(dllexport) void bar (void);

/**Benutzen Sie thread-Argumente nur bei statischen Speicherdaten. **/

__declspec(thread) int th;

int __declspec(thread) th1;


declspec (Beispiel)
Verwendung von __declspec in VCL-Klassendeklarationen

// Forward-Deklarationen

class __declspec(delphiclass) MyVclClass;

class __declspec(delphireturn) MyString;

MyVclClass* MyVclClassFunc();

MyString* MyStringFunc();

class MyClass : public TVclClass

{   /* class definition */ };

class MyString: public AnsiString

{  /* class definition */ };


default (Schlüsselwort)
default

Siehe auch	Beispiel	Schlüsselwörter

Syntax

switch ( <switch_variable> ){

  case <konstantenausdruck> : <anweisung>; [break;]

    .

    .

    .

  default : <anweisung>;

}

Beschreibung

Die Anweisung default wird zusammen mit switch benutzt.

Wenn innerhalb der switch-Anweisung keine passende case-Konstante, aber die Anweisung default vorhanden ist, so wird der Programmablauf an dieser Stelle fortgesetzt.

Ist default nicht vorhanden, so wird der komplette switch-Anweisungsblock übersprungen.


__dispid

Siehe auch	Schlüsselwörter	Beispiel

Syntax

__dispid(konstanter int ausdruck)

Beschreibung

Eine Elementfunktion die im __automated -Abschnitt einer Klasse deklariert wurde, kann eine optionale Direktive __dispid(constant int expression) enthalten. Die Direktive muss nach der schließenden Klammer der Parameterliste deklariert werden.

Der Ausdruck konstanter int ausdruck liefert die Verteiler-ID für die OLE-Automatisierung der Elementfunktion oder Eigenschaft. Wenn eine dispid-Directive nicht verwendet wird, nimmt der Compiler automatisch eine Zahl, die eins größer ist als die größte Verteiler-ID, die von einer Elementfunktion oder Eigenschaft in der Klasse und deren Basisklassen verwendet wird.

Die Angabe einer Verteiler-ID, die bereits verwendet wird, führt zu einem Compilierzeitfehler.


do

Siehe auch	Beispiel	Schlüsselwörter

Syntax

do <anweisung> while ( <bedingung> );

Beschreibung

Die Anweisung do wird solange ausgeführt, bis die <bedingung> zu false ausgewertet wird.

<anweisung> wird solange wiederholt ausgeführt, wie der Wert von <bedingung> gleich true bleibt.

Da die Bedingung nach jeder Ausführung von <anweisung> geprüft wird, wird die Schleife mindestens einmal durchlaufen.


do (Beispiel)

/* In diesem Beispiel wird der Bediener zur Eingabe eines Paßworts */

/* aufgefordert. Diese Aufforderung wird so oft wiederholt, bis    */

/* ein Paßwort eingegeben wird, das dem in Vergleichswort          */

/* gespeicherten entspricht.                                       */

#include <stdio.h>

#include <string.h>

int main ()

{

  char *vergleichswort = "paßwort";

  char *passwort;

  do {

    printf ("Paßwort eingeben: ");

    scanf(passwort);

  } while (strcmp(passwort,vergleichswort));

  return 0;

}


double, Schlüsselwort
double

Siehe auch	Schlüsselwörter

Syntax

[long] double <bezeichner>

Beschreibung

Der Typspezifizierer double wird bei der Deklaration von Namen verwendet, die für Gleitkommatypen stehen. Der optionale Modifizierer long bewirkt, daß die Gleitkommawerte eine höhere Stellenzahl (also Genauigkeit) haben.

Wenn Sie das Schlüsselwort double in einem Programm verwenden, so linkt die C++Builder IDE automatisch die mathematische Gleitkommabibliothek mit dazu.


enum

Siehe auch	Beispiel	Schlüsselwörter

Syntax

enum [<tag_name>] {<konstantenname> [= <wert>], ...} [variablen_liste];

<tag_name> ist ein optionaler Tag-Name, der die Aufzählungsmenge bezeichnet.

<konstantenname> ist der Name einer Konstanten, die optional den Wert <wert> erhalten kann. Diese Konstanten werden auch Aufzählungskonstanten genannt.

<wert> muß vom Typ int sein. Fehlt die Angabe <wert>, so wird dafür

<vorgänger> + 1

angenommen, dabei ist <vorgänger> der Wert der in der Liste davor stehenden int-Konstante. Für die erste Konstante wird in der Voreinstellung der Wert Null angenommen.

<variablen_liste> ist eine optionale Variablenliste; diese Variablen werden damit als von diesem Aufzählungstyp deklariert.

Beschreibung

Das Schlüsselwort enum wird verwendet, um eine Menge von int-Konstanten zu definieren; eine solche Menge wird Aufzählungsdatentyp genannt.

Ein Aufzählungstyp stellt mnemonische Bezeichnungen für die Elemente einer Menge von int-Werten zur Verfügung. Wenn Sie allerdings die Option Enum als Integer behandeln (Menü: Optionen/Projekt/16-Bit-Compiler/Code-Generierung)ausschalten oder die Kommandozeilenoption -b angeben, so reserviert C++Builder für Aufzählungstypen nur ein Byte.

Werte von Aufzählungstypen werden immer als vom Typ int interpretiert, falls es der Wertebereich zuläßt; wenn sie jedoch nicht vom Typ int sind, werden sie in Ausdrücken zu int erweitert. Die Bezeichner in einer Aufzählungsliste sind implizit vom Typ signed char, unsigned char oder int, abhängig vom Wertebereich.

In C kann einer Aufzählungstypvariablen jeder Wert vom Typ int zugewiesen werden; es findet keine darüberhinausgehende Typprüfung statt. In C++ dagegen kann einer solchen Variablen nur eine ihrer Aufzählungskonstanten zugewiesen werden.

In C++ können Sie das Schlüsselwort enum auch weglassen, wenn <tag_name> in diesem Gültigkeitsbereich keine andere Bedeutung hat. Sie können auch <tag_name> weglassen, wenn Sie später keine weiteren Variablen dieses Aufzählungstyps deklarieren wollen.

Fehlt die Angabe <wert>, so erhält die erste Aufzählungskonstante den Wert Null. Alle folgenden Konstanten ohne Initialisierer erhalten dann einen jeweils um 1 höheren Wert. Für <wert> kann jeder Ausdruck eingesetzt werden, der einen positiven oder negativen int-Wert (ggfs. nach Vorzeichenerweiterung) ergibt. Diese Werte sind normalerweise eindeutig, doppelt vorkommende sind aber erlaubt.

Die Tag-Namen von Aufzählungstypen liegen im selben Namensraum wie die Tag-Namen von Strukturen und Varianten. Die Namen der Aufzählungskonstanten liegen im selben Namensraum wie normale Variablennamen.

In C++ haben die Namen von Aufzählungskonstanten, die in einer Klasse deklariert sind, den Gültigkeitsbereich dieser Klasse.


Beispiele

enum tage { so, mo, di, mi, do, fr, sa } irgendein_tag;

Die vorstehende Deklaration ergibt den eindeutigen ganzzahligen Typ tage, die Variable irgendein_tag von ebendiesem Typ sowie eine Menge von Aufzählungskonstanten (so, mo, ...) mit ganzzahligen Werten.

enum modi { LETZTER_MODUS = -1, BW40=0, C40, BW80, C80, MONO = 7 };

/*

   "modi" ist der Tag-Name des Typs.

   "LETZTER_MODUS", "BW40", "C40" usw. sind die Namen der Konstanten.

   Der Wert von C40 ist 1 (BW40 + 1); BW80 = 2 (C40 + 1) usw.

*/


_ _except

Siehe auch	Schlüsselwörter

Syntax

__except (ausdruck) zusammengesetzte_anweisung

Beschreibung

Mit dem Schlüsselwort __except wird eine Aktion spezifiert, die durchgeführt werden soll, wenn die durch ausdruck spezifizierte Exception eingetreten ist.


explicit

Siehe auch

Syntax

explicit <single-parameter constructor declaration>

Beschreibung

Normalerweise kann einer Klasse mit einen Konstruktor, der genau einen Parameter enthält, ein Wert zugewiesen werden, der zum Konstruktortyp paßt. Dieser Wert wird automatisch (implizit) in ein Objekt der Klasse konvertiert, an die die Zuweisung erfolgte. Sie können das Erscheinen dieser Art von impliziter Umwandlung verhindern, indem Sie den Konstruktor der Klasse mit dem Schlüsselwort explicit deklarieren. An alle Objekte dieser Klasse müssen dann Werte zugewiesen werden, die vom Klassentyp sind, alle anderen Zuweisungen ergeben einen Compilerfehler.

An Objekte der folgenden Klasse können Werte zugewiesen werden, die zu dem Konstruktortyp oder zu dem Klassentyp passen:

class X {

public:

  X(int);

  X(const char*, int = 0);

};

Dann sind die folgenden Zuweisungen gültig.

void f(X arg) {

  X a = 1;

  X B = "Jessie";

  a = 2;

}

Objekten der folgenden Klassen können jedoch Werte zugewiesen werden, die nur zum Klassentyp passen:

class X {

public:

  explicit X(int);

  explicit X(const char*, int = 0);

};

Die Konstruktoren des Typs explicit benötigen dann, daß die Werte in den folgenden Zuweisungen zu den Klassentypen umgewandelt werden, an die sie zugewiesen werden.

void f(X arg) {

  X a = X(1);

  X b = X("Jessie",0);

  a = X(2);

}


_export, _ _export

Siehe auch	Schlüsselwörter

Form 1

class _export <klassen_name>

Form 2

return_type _export <funktionsname>

Form 3

data_type _export <daten_name>

Beschreibung

Die Modifizierer _export und __export werden in Funktionsdefinitionen eingesetzt, um die Funktionen zu exportieren.

Der Linker trägt Funktionen, die durch _export oder __export gekennzeichnet sind, in die Exporttabelle des Moduls ein.

Die Verwendung der Schlüsselworte _export bzw. __export macht einen EXPORTS-Abschnitt in Ihrer Moduldefinitionsdatei überflüssig.

Funktionen, die ohne _export und __export definiert sind, werden mit einem kürzeren Prolog und Epilog versehen, was zu einer kleineren Objektdatei und etwas schnellerer Ausführung führt.

Hinweis:	Wenn Sie _export oder __export verwenden, um eine Funktion zu exportieren, so wird diese Funktion über ihren Namen und nicht über ihre Ordinalzahl exportiert. (Das Exportieren über die Ordinalzahl ist meist effizienter.)

Wenn Sie die Voreinstellung verschiedener Attribute ändern wollen, benötigen Sie eine Moduldefinitionsdatei.


Compiler-Optionen und das Schlüsselwort _export

Siehe auch	Schlüsselwörter

Die folgende Tabelle gibt einen Überblick über die Wirkung des Zusammenspiels der verschiedenen Windows-Optionen mit dem Schlüsselwort _export:

Compiler-Option: *	-tW·oder -tWD	-tWE·oder -tWDE	-tW·oder -tWD	-tWE·oder -tWDE	-tW·oder -tWD	-tWE·oder -tWDE	-tW·oder -tWD	-tWE·oder -tWDE
Funktion·als _export deklariert?	Ja	Ja	Ja	Ja	Nein	Nein	Nein	Nein
Funktion·in EXPORTS aufgeführt?	Ja	Ja	Nein	Nein	Ja	Ja	Nein	Nein
Funktion ist· exportierbar?	Ja	Ja	Ja	Ja	Ja	Nein	Ja	Nein
Wird die Funktion exportiert?	Ja	Ja	Ja	Ja	Ja	Ja	Nein	Nein
*	Oder das Äquivalent für 32-Bit Textbildschirmanwendungen.


Exportierbare Funktionen in DLLs

Siehe auch

Es gibt zwei Möglichkeiten, eine Funktion f1( ) in einer DLL als exportierbar zu compilieren und sie dann zu exportieren.

Compilieren Sie die DLL in der IDE und führen Sie f1( ) im Abschnitt EXPORTS der Moduldefinitionsdatei auf, oder

Deklarieren Sie die Funktion f1( ) mit dem Schlüsselwort _export.


Verwendung des Schlüsselworts _export mit C++ Klassen

Siehe auch	Schlüsselwörter

Wenn Sie eine Klasse mit _export deklarieren, exportiert der Compiler alle ihre Elementfunktionen (außer Inline-Funktionen) und statischen Datenelemente.

Wenn Sie eine Klasse in einer Include-Datei deklarieren, die sowohl in die DLL-Quelldateien als auch in die Quelldateien des Anwendungsprogramms, das die DLL benutzt, eingebunden wird, so müssen Sie die Klasse beim Compilieren der DLL als _export deklarieren.

Um dies zu erreichen, können Sie das Makro __DLL__ verwenden, das der Compiler definiert, wenn er eine DLL erzeugt.


extern (Schlüsselwort)
extern

Siehe auch	Beispiel	Schlüsselwörter

Syntax

extern <daten_definition> ;

[extern] <funktions_prototyp> ;

Beschreibung

Der Modifizierer teilt dem Compiler mit, daß der Speicherplatz und der Initialisierungswert einer Variablen bzw. der Rumpf einer Funktion effektiv in einem anderen Quelltextmodul definiert wird. Funktionen, die mit extern deklariert sind, sind in allen Quelltextdateien eines Programms sichtbar, sofern Sie die Funktion nicht als static definieren.

Bei Funktionsprototypen ist das Schlüsselwort extern optional.

Schreiben Sie in C++ extern "c", um zu verhindern, daß Funktionsnamen verändert werden.


Beispiel

extern int _fmode;

extern void Faktoren(int n);

extern "c" void cfunk(int);


Parametertypen und möglicherweise verwendete Register

Um zu entscheiden, welche Parameter in Registern übergeben werden, geht der Compiler nach folgenden Regeln vor.

Parametertyp	Register

char (signed und unsigned)	AL, DL, BL

integer (signed und unsigned)	AX, DX, BX

long (signed und unsigned)	DX:AX

near-Zeiger	AX, DX, BX

Bei jeder Funktion können nur drei Parameter in Registern übergeben werden.

Gehen Sie nicht davon aus, daß die Registerzuordnung die Reihenfolge der Funktionsparameter widerspiegelt. far-Zeiger sowie Parameter vom Typ Variante, Struktur oder Gleitkommazahl (float, double und long) werden auf den Stack gelegt.


_fastcall und __fastcall
_fastcall, _ _fastcall

Siehe auch	Schlüsselwörter

Syntax

return-value _fastcall function-name(parm-list)

return-value __fastcall function-name(parm-list)

Beschreibung

Beschreibung

Die Modifizierer__fastcall wird bei der Deklaration von Funktionen benutzt, die eine Parameterübergabe in Registern erwarten. Die ersten drei Parameter werden (von links nach rechts) in EAX, EBX, und EDX übergeben, wenn Sie in das Register passen. Die Register werden nicht verwendet, wenn der Parameter ein Gleitkommatyp oder struct-Typ ist. Alle Formular-Klassenelementfunktionen müssen die Konvention __fastcall verwenden.

Der Compiler behandelt diese Aufrufkonvention als Angabe einer anderen Sprache, ähnlich wie bei _cdecl und _pascal.

Funktionen, die mit _cdecl oder _pascal deklariert werden, können nicht gleichzeitig auch mit _fastcall deklariert werden, weil sie die Parameter über den Stack übergeben. Weiterhin können die Modifizierer _fastcall nicht zusammen mit _export verwendet werden.

Der Compiler stellt den Namen von _fastcall-Funktionen das At-Zeichen ("@") voran. Dies gilt sowohl für nicht veränderte C-Funktionsnamen als auch für veränderte C++ Funktionsnamen.

Hinweis:	Der Modifizierer __fastcall ist dem Ergänzen von Namen ausgesetzt. Eine entsprechende Beschreibung finden Sie unter der Option -VC option.


_ _finally

Siehe auch	Schlüsselwörter

Syntax

__finally {zusammengesetzte anweisung}

Beschreibung

Das Schlüsselwort _ _finally spezifiziert eine Aktion, die unabhängig davon durchgeführt werden soll, wie der Programmablauf durch das vorangehende _ _try-Konstrukt endet.

Das Schlüsselwort _  _finally kann nur in C-Programmen verwendet werden.


float, Schlüsselwort
float

Siehe auch	Schlüsselwörter

Syntax

float <bezeichner>

Beschreibung

Der Typspezifizierer float legt fest, daß ein Name für einen Gleitkommadatentyp steht.

Typ	Größe	Wertebereich

float	32 Bit 	3,4 * (10-38) bis 3,4 * (10+38)

Die IDE von C++ linkt automatisch die mathematische Gleitkommabibliothek zu Ihrem Programm hinzu, wenn Sie Gleitkommawerte oder -operatoren verwenden.


for (Schlüsselwort)
for

Beispiel	Schlüsselwörter

Syntax

for ( [<initialisierung>] ; [<bedingung>] ; [<inkrement>] )  <anweisung>

Beschreibung

Die Anweisung for dient zum Programmieren einer Schleife.

<condition> wird vor dem ersten Einstieg in den Block geprüft.

<anweisung> wird wiederholt so oft ausgeführt, bis der Wert von <bedingung> false ist.

Vor dem ersten Durchlauf der Schleife werden die Variablen für die Schleife durch <initialisierung> initialisiert.

Nach jedem Durchlauf der Schleife wird die Laufvariable durch <inkrement> erhöht. (Deshalb ist ++j in diesem Fall funktionell gleichwertig mit j++.)

In C++ kann <initialisierung> ein Ausdruck oder eine Deklaration sein.

Der Gültigkeitsbereich der dort deklarierten Bezeichner erstreckt sich nur bis zum Ende der for-Anweisung.

Eine im Ausdruck for-initialisierung definierte Variable liegt nur dann im Bereich, wenn sie sich innerhalb des for-Blockes befinden. Siehe die Beschreibung der Option -Vd.

Alle drei Ausdrücke sind optional. Wenn <bedingung> weggelassen wird, so wird dafür der Wert true angenommen.


Beispiele

// Ein Beispiel des Beireiches von Variablen in for-Ausdrücken

// Dieses Beispiel compiliert nur, wenn sie die Option -Vd verwenden.

#include <iostream.h>

int main() {

   for (int i = 0; i < 10; i++)

      if (i == 8)

      cout << "\ni = " << i;

return i;  // Undefiniertes Symbol ‘i’ in  der Function main().

}


friend (Schlüsselwort)
friend

Beispiel	Schlüsselwörter

Syntax

friend <bezeichner>;

Beschreibung

Das Schlüsselwort friend dient zur Deklaration einer Funktion oder Klasse, die volle Zugriffsrechte auf private und geschützte Elemente einer fremden Klasse hat, obwohl sie selbst keine Elementfunktion dieser fremden Klasse ist.

In jeder anderen Beziehung ist eine friend-Funktion eine normale Funktion, was Gültigkeitsbereich, Deklarationen und Definitionen angeht.


Beispiel

class sterne {

   friend class galaxis;

   int groesse;

   int sternfunktion(void);

};

class galaxis {

   long int anzahl_der_sterne;

   void sterne_groesse(stars&);

   void sterne_funk(stars*);

}


goto (Schlüsselwort)
goto

Beispiel	Schlüsselwörter

Syntax

goto <bezeichner> ;

Beschreibung

Die Anweisung goto dient dazu, die Ablaufkontrolle zu dem durch <bezeichner> angegebenen lokalen Label springen zu lassen.

Labels werden immer durch einen Doppelpunkt abgeschlossen.


Beispiel

Nochmal:       /* Dies ist das Label */

;

.

.

.

goto Nochmal;


if (Schlüsselwort)
if

Siehe auch	Beispiel	Schlüsselwörter

Syntax

if ( <bedingung> )  <anweisung1>;

if ( <bedingung> )  <anweisung1>;

   else  <anweisung2>;

Beschreibung

Die Anweisung if dient zum Programmieren einer Bedingungsabfrage.

Sie können Variablen in einem bedingten Ausdruck deklarieren. Beispielsweise ist

   if (int val = func(arg))

ein gültige Syntax. Die Variable val befindet sich in dem Gültigkeitsbereich der if-Anweisung und wird in dem else-Block weitergeführt, sofern dieser vorhanden ist.

Der Ausdruck <bedingung> muß sich zu einem Wert vom Typ bool auswerten lassen, andernfalls ist die Bedingung ungültig.

Wenn die Auswertung von <bedingung> den Wert true ergibt, so wird <anweisung1> ausgeführt.

Wenn <bedingung> false ergibt, so wird <anweisung2> ausgeführt.

Die Klausel else ist optional, es dürfen aber zwischen einer if-Anweisung und else keine weiteren Anweisungen stehen.

Die Präprozessor-Direktiven #if und #else sehen der if- /if-else-Anweisung sehr ähnlich, haben aber eine völlig andere Wirkung. Sie steuern nämlich, welche Zeilen des Quelltextes compiliert und welche ignoriert werden.


Beispiele

if (int val = func(count)) { /* Anweisungen */ }

else {

   /* Nehme andere Aktion */

   cout << "val is false"

   }


_import, _ _import

Schlüsselwörter

Form 1

class _import <klassen_name>

class __import <klassen_name>

Form 2

return_type _import <funktionsname>

return_type __import <funktionsname>

Form 3

data_type _import <daten_name>

data_type __import <daten_name>

Beschreibung

Dieses Schlüsselwort kann als Klasse, Funktion oder Daten-Modifizierer in 32-Bit-Programmen verwendet werden.


inline, Schlüsselwort
inline

Beispiel	Schlüsselwörter

Syntax

inline <datentyp> <klasse>_<funktion> (<parameter>) { <anweisungen>; }

Beschreibung

Das Schlüsselwort inline dient zum Deklarieren oder Definieren von C++ Inline-Funktionen.

Zu Inline-Funktionen macht man am besten nur kleine, sehr häufig aufgerufene Funktionen.


Beispiel

inline char* cat_funk(void) { return char*; }


int (Schlüsselwort)
int

Siehe auch	Schlüsselwörter

Syntax

[signed|unsigned] int <bezeichner> ;

Beschreibung

Der Typ-Spezifizierer int dient zum Definieren eines int-Datentyps.

int-Variablen können signed (Voreinstellung) oder unsigned sein.


long, Schlüsselwort
long

Siehe auch	Schlüsselwörter

Syntax

long [int] <bezeichner> ;

[long] double <bezeichner> ;

Beschreibung

Bei Verwendung zur Modifizierung des Typs int wird bewirkt, daß zur Speicherung des Typ int doppelt so viele Bytes an Speicherplatz verfügbar sind.

Bei Verwendung zur Modifizierung des Typs double wird ein Gleitkomma-Datentyp mit einer Genauigkeit von 80 Bit statt 64 Bit definiert.

Die IDE von C++Builder linkt automatisch die mathematische Gleitkommabibliothek zu Ihrem Programm hinzu, wenn Sie darin irgendwo Gleitkommawerte oder -operatoren verwenden.


mutable

Siehe auch	Beispiel	Schlüsselwörter

Syntax

mutable <variable name>;

Beschreibung

Variablen mit dem Attribut mutable lassen sich auch dann ändern, wenn sie in einem mit const qualifizierten Ausdruck erscheinen.

Verwendung des Schlüsselwortes mutable

Nur Datenelemente von Klassen können als mutable deklariert werden. mutable darf nicht kombiniert werden mit static oder const. Mit mutable lassen sich Datenelemente als durch const-Elementfunktionen veränderbar kennzeichnen, - was normalerweise nicht möglich ist.


Beispiel

#include <iostream.h>

class Alpha {

   mutable int count;

   mutable const int* iptr;

public:

   int func1(int i = 0) const { // Eine Funktion die keine const Argumente ändern soll.

      count = i++;  // Aber count läßt sich ändern.

      iptr = &i;

      cout << *iptr;

      return count;

      }

};

int main(void) {

   Alpha a;

   a.func1(0);

   return 0;

   }


operator (Schlüsselwort)
operator

Siehe auch	Beispiel	Schlüsselwörter

Syntax

operator <operatorsymbol>( <parameter> )

{

   <anweisungen>;

}

Beschreibung

Das Schlüsselwort operator dient dazu, eine neue (überladene) Wirkung dieses Operators zu definieren. Wenn der Operator als Elementfunktion überladen wird, ist nur ein Argument zulässig, da das erste Argument implizit *this ist.

Wenn Sie einen Operator als friend -Funktion überladen, so können Sie zwei Argumente angeben.


pascal, _pascal und __pascal (Schlüsselwörter)
pascal, _pascal,  _ _pascal

Schlüsselwörter

Syntax

pascal <datendefinition/funktionsdefinition> ;

_pascal <datendefinition/funktionsdefinition> ;

__pascal <datendefinition/funktionsdefinition> ;

Beschreibung

Die Schlüsselwörter pascal, _pascal und __pascal dienen dazu, eine Variable oder Funktion mit den Namenskonventionen der Sprache Pascal (Namen werden in Großbuchstaben geschrieben) zu definieren.

Wenn eines dieser Schlüsselworte in einer Funktionsdefinition benutzt wird, so wird dadurch außerdem festgelegt, daß die Parameterübergabe nach Pascal-Konvention erfolgt (Der erste Parameter kommt zuerst auf den Stack, die aufrufende Funktion bereinigt den Stack).

In C++ Programmen werden die Namen von Funktionen, die mit den Modifizierer pascal deklariert sind, trotzdem verändert.


private (Zugriffsspezifizierer)
private

Siehe auch	Schlüsselwörter

Syntax

private: <deklarationen>

Beschreibung

Auf ein privates Element kann nur von Elementfunktionen und von Friend-Funktionen der Klasse, in der es deklariert ist, aus zugegriffen werden.

Die Elemente einer Klasse sind standardmäßig private Elemente.

Sie können den standardmäßigen Zugriff auf Strukturen mit privat oder geschützt verhindern, nicht jedoch den standardmäßigen Zugriff auf Varianten.

Friend-Deklarationen werden von diesem Zugriffsspezifizierer nicht berührt.


protected (Zugriffsspezifizierer)
protected

Siehe auch	Schlüsselwörter

Syntax

protected: <declarations>

Beschreibung

Auf ein protected-Element kann nur ausgehend von Elementfunktionen und von Friend-Funktionen der Klasse, in der es deklariert ist, zugegriffen werden. Außerdem kann auf ein protected-Element ausgehend von solchen Klassen zugegriffen werden, die von derjenigen abgeleitet sind, in der es deklariert ist.

Sie können den standardmäßigen Zugriff auf Strukturen mit private oder protected verhindern, nicht jedoch den standardmäßigen Zugriff auf Varianten.

Friend-Deklarationen werden von diesem Zugriffsspezifizierer nicht berührt.


public (Zugriffsspezifizierer)
public

Siehe auch	Schlüsselwörter

Syntax

public: <declarations>

Beschreibung

Auf ein public-Element kann von jeder Funktion aus zugegriffen werden.

Die Elemente einer Struktur oder Variante sind standardmäßig public-Elemente.

Sie können den standardmäßigen Zugriff auf Strukturen mit private oder protected verhindern, nicht jedoch den standardmäßigen Zugriff auf Varianten.

Friend-Deklarationen werden von diesen Zugriffsspezifizierern nicht berührt.


__property

Siehe auch	Schlüsselwörter	Beispiel

Syntax

<property declaration> ::=

      __property <type> <id> [ <prop dim list> ] = "{" <prop attrib list> "}"

  <prop dim list> ::= "[" <type> [ <id> ] "]" [ <prop dim list> ]

  <prop attrib list> ::= <prop attrib> [ , <prop attrib list> ]

  <prop attrib> ::=     read = <data/function id>

  <prop attrib> ::=     write = <data/function id>

  <prop attrib> ::=     stored = <data/function id>

  <prop attrib> ::=     stored = <boolean constant>

  <prop attrib> ::=     default = <constant>

  <prop attrib> ::=     nodefault

  <prop attrib> ::=     index = <const int expression>

Beschreibung

Verwenden Sie __property um speziellen Lese-/Schreib-Zugriff mit einem Bezeichner zu verknüpfen.

Eine Eigenschaftsdeklaration in einer Klasse definiert ein benanntes Attribut der Klasse, sowie Vorgänge die mit dem Lesen und Ändern des Attribut verknüpft sind. Beispiele für Eigenschaften sind der Titel eines Formulars, die Schriftgröße, der Name eine Datenbanktabelle, und so weiter. Eine Eigenschaft kann von jedem beliebigen Typen sein, außer einem Dateityp.

Eigenschaften stellen eine natürliche Erweiterung von Datenelementen in einem Objekt dar. Beide können verwendet werden um Attribute eines Objekts darzustellen. Während jedoch Datenelemente hauptsächlich Speicherstellen darstellen, die nach belieben untersucht und verändert werden können, stellen Eigenschaften größere Kontrolle beim Zugriff auf Attribute bereit. Eigenschaften stellen einen Mechanismus für die Verknüpfung von Lese- und Schreiboperationen mit Attributen bereit und sie erlauben es, das Attribute berechnet werden.

Bei Eigenschafts-Arrays (wenn <prop dim list> verwendet wird) kann der Index des Arrays von jedem beliebigen Typ sein.

Eigenschaften können nur in Klassen deklariert werden.

Der Bezeichner in einer Lese-/Schreibanweisung muß ein Datenelement oder eine Elementfunktion sein.


__published

Siehe auch	Schlüsselwörter

Syntax

__published: <declarations>

Beschreibung

Verwenden Sie das Schlüsselwort __published um Eigenschaften anzugeben, die im Objektinspektor angezeigt werden sollen. Nur die Klassen, die von TObject abgeleitet wurden, können __published-Abschnitte enthalten.

Hinweis: Die Deklarationen im _published -Abschnitt einer von TObject abgeleiteten Klasse, werden vom Formular-Editor in der integrierten Entwicklungsumgebung verwaltet. Sie sollten die Deklarationen in diesem Abschnitt nicht bearbeiten.

Die Sichtbarkeitsregeln für published-Elemente sind die gleichen wie die für public-Elemente. Der einzige Unterschied zwischen published- und public-Elementen ist, daß für Datenelemente und Eigenschaften, die im Abschnitt __published deklariert werden, Laufzeit-Typinformationen (Run-Time Type Information, RTTI) im Delphi-Stil erzeugt werden. RTTI ermöglicht einer Anwendung die dynamische Abfrage von Datenelementen, Elementfunktionen und Eigenschaften eines ansonsten unbekannten Klassentyps.

In __published-Abschnitten sind keine Konstruktoren oder Desktruktoren erlaubt. Eigenschaften, eingebaute Pascal-, oder von der VCL abgeleitete Datenelemente, Elementfunktionen und Closures sind erlaubt.


register, Schlüsselwort
register

Beispiel	Schlüsselwörter

Syntax

register <datendefinition> ;

Beschreibung

Der Speicherklassenspezifizierer register weist den Compiler an, die deklarierte Variable (falls möglich) in einem CPU-Register zu speichern, um die Zugriffsgeschwindigkeit zu erhöhen und das Programm kürzer zu machen.

Variablen, die mit register deklariert sind, haben globale Lebensdauer.

Hinweis: Der C++Builder Compiler kann anforderungen nach Registerspeicher ignorieren. Die Allokation von Registern basiert auf den Analysen des Compilers und darauf, wie eine Variable verwender wird


Beispiel

register int i;


return (Schlüsselwort)
return

Beispiel	Schlüsselwörter

Syntax

return [ <ausdruck> ] ;

Beschreibung

Die Anweisung return dient dazu, die aktuelle Funktion zu verlassen und zur aufrufenden Funktion zurückzukehren, wobei optional ein Wert zurückgeliefert werden kann.


Beispiel

double quadrat(double x)

{

  return (x*x);

}


short

Siehe auch	Beispiel	Schlüsselwörter

Syntax

short int <variable> ;

Beschreibung

Der Typ-Modifizierer short wird verwendet, wenn die Größe einer Variablen kleiner als beim Typ int sein soll. Dieser Modifizierer kann auf den Basistyp int angewendet werden.

Fehlt in einer Deklaration die Angabe des Basistyps, so wird dafür int angenommen.


Beispiele

short int i;

short     i;    /* same as "short int i;" */


signed (Typmodifizierer)
signed

Siehe auch	Beispiel	Schlüsselwörter

Syntax

signed <typ> <variable> ;

Beschreibung

Der Typ-Modifizierer signed wird verwendet, wenn der Wert einer Variablen sowohl positiv als auch negativ sein kann. Dieser Modifizierer kann auf die Basistypen int, char, long und short angewendet werden.

Fehlt in einer Deklaration die Angabe des Basistyps, so wird dafür int angenommen.


Beispiel

signed   int       i;    /* Voreinstellung ist signed */

signed             i;    /* Gleichwertig mit "signed int i;"  */

unsigned long int  l;    /* int ist erlaubt, aber nicht erforderlich */

signed   char      ch;   /* Voreinstellung ist unsigned */


Oparator sizeof (Schlüsselwort)
Operator sizeof

Siehe auch	Beispiel 	Operators

Der Operator sizeof ist auf zwei verschiedene Arten verwendbar:

sizeof <ausdruck>

sizeof ( <typ> )

Das Ergebnins ist in beiden Fällen einen Konstante vom Typ int, die die Größe des vom Operanden (bis auf einige Ausnahmen abhängig vom Typ) verwendeten Speicherplatzes in Bytes angibt. Die Anzahl des für jeden Typ reservierten Speicherplatz hängt von der Maschine ab.

Bei seiner ersten Verwendung wird der Typ vom Ausdruck des Operanden ohne eine Auswertung des Ausdrucks (und damit ohne Seiteneffekte) bestimmt. Wenn der Operand vom Typ char (signed or unsigned) ist, liefert sizeof den Wert 1. Wenn der Operand zwar vomTyp Array, aber kein Parameter ist, ist das Ergebnis die gesamte Anzehl von Bytes im Array (mit anderen Worten wird ein Array-Name nicht in einen Zeigertyp konvertiert). Die Anzahl von Elementen in einem Array ist sizeof array/ sizeof array[0] .

Wenn der Operand ein Parameter ist, der als Arraytyp oder als Funktionstyp deklariert wurde, liefert  sizeof die Größe des Zeigers. Bei Strukturen oder Varianten liefert sizeof die Gesamtanzahl an Bytes einschließlich allen Inhaltes.

Sie können sizeof nicht mit folgenden Ausdrücken verwenden: Funktionstypen, unvollständigen Typen, oder einen lvalue, der ein Objekt eines Bitfeldes bezeichnet.

Der Integertyp des Ergebnisses von sizeof ist size_t, das in in stddef.h definiert ist.

Sie können sizeof in Anweisungen des Präprozessors verwenden. Dies ist spezifisch für C++Builder.

In C++ liefert sizeof(classtype) die Größe des Objektes, wobei classtype von einigen Basisklassen abgeleitet ist (Denken Sie daran, daß das die Größe der Basisklasse mit einschließt).

Beispiel für den Operator sizeof

/*  VERWENDEN SIE DEN OPERATOR size, UM DIE GRÖßEN VERSCHIEDERER DATENTYPEN ZU ERHALTEN.  */

#include <stdio.h>

struct st {

   char *name;

   int age;

   double height;

      };

struct st St_Array[]= {  /* EIN ARRAY VON structs */

   { "Jr.",     4,  34.20 },  /* ST_Array[0] */

   { "Suzie",  23,  69.75 },  /* ST_Array[1] */

   };

int main() {

   long double LD_Array[] = { 1.3, 501.09, 0.0007, 90.1, 17.08 };

   printf("\nNumber of elements in LD_Array = %d",

           sizeof(LD_Array) / sizeof(LD_Array[0]));

   /****  DIE ANZAHL DER ELEMENTE IN DEM ST_Array. ****/

   printf("\nSt_Array has %d elements",

           sizeof(St_Array)/sizeof(St_Array[0]));

   /****  DIE ANZAHL DER BYTES IN JEDEM ELEMENT VON ST_Array.  ****/

   printf("\nSt_Array[0] = %d", sizeof(St_Array[0]));

   /****  DIE GESAMTZAHL VON BYTES IM ST_Array.  ****/

      printf("\nSt_Array=%d", sizeof(St_Array));

   return 0;

   }

Ausgabe

Number of elements in LD_Array = 5

St_Array has 2 elements

St_Array[0] = 16

St_Array= 32


static (Schlüsselwort)
static

Beispiel	Schlüsselwörter

Syntax

static <datendefinition> ;

static <funktionsdefinition> ;

Beschreibung

Der Speicherklassen-Spezifizierer static ermöglicht es bei lokalen Variablen einer Funktion, deren letzten Wert zwischen aufeinanderfolgenden Aufrufen der betreffenden Funktion unverändert zu erhalten. Eine static-Variable verhält sich wie eine lokale Variable, hat aber die Lebensdauer einer externen Variable.

In einer Klasse können Elementfunktionen einer globalen Klasse als static deklariert werden. Für alle Objekte der Klasse existiert nur eine Kopie der static Daten.

Eine static Elementfunktion einer globalen Klasse verfügt über externe Bindung. Ein Element einer lokalen Klasse verfügt über keine Bindung. Eine static Elementfunktion ist nur mit den Klassen verbunden, in denen Sie deklariert ist. Deshalb können derartige Elementfunktionen nicht virtual sein.

Statische Elementfunktionen können nur andere static Elementfunktionen aufrufen und nur auf static Daten zugreifen. Derartige Elementfunktionen haben keinen Zeiger vom Typ this.


Beispiele

static int i;

static void printnewlinen(void) {}


_stdcall und __stdcall
_stdcall,  __stdcall

Schlüsselwörter

Syntax

__stdcall <funktions_name>

_stdcall <funktions_name>

Beschreibung

Die Schlüsselwörter _stdcall und __stdcall veranlassen den Compiler, für Funktionsaufrufe Befehle gemäß der Standard-Aufrufkonvention zu erzeugen. Solche Funktionsaufrufe sind schneller und benötigen weniger Speicherplatz. In Funktionsaufrufen müssen die Argumente nach Anzahl und Typ korrekt angegeben werden; dies steht im Gegensatz zur normalen C-Syntax, bei der eine variable Anzahl von Argumenten erlaubt ist. Derartige Funktionen halten sich an die Standardkonvention bei der Übergabe von Komponenten unter WIN32.

Hinweis: Der Modifizierer __stdcall ist Gegenstand für das Ergängen von Namen. Siehe die Beschreibung der Option -VC.


struct (Schlüsselwort)
struct

Siehe auch	Beispiel	Schlüsselwörter

Syntax

struct [<strukturtyp_name>] {

  [<typ> <variablenname[, variablenname, ...]>] ;

    .

    .

    .

} [<struktur_variablen>] ;

Beschreibung

Strukturen werden benutzt, um mehrere Variablen in einem einzigen "Datenrecord" zusammenzufassen.

<strukturtyp_name>	Optionaler Tag-Name, der den Typ der Struktur bezeichnet.

<struktur_variablen>	Datendefinitionen, ebenfalls optional.

Obwohl <strukturtyp_name> und <struktur_variablen> optional sind, ist eine der beiden Angaben zwingend erforderlich.

Elemente im Record werden durch Angabe ihres Typs (<typ>), gefolgt von einem oder mehreren Variablennamen (durch Kommas getrennte Liste mit <variablenname>) definiert.

Unterschiedliche Variablentypen werden durch ein Semikolon getrennt.

Zum Zugriff auf einzelne Elemente in einer Struktur dient der Elementselektor (.).

Um weitere Variablen desselben Strukturtyps zu deklarieren, schreibt man das Schlüsselwort struct, gefolgt von <strukturtyp_name>, gefolgt von den Variablennamen.

Hinweis: C++Builder erlaubt den Gebrauch von anonymen struct, die in andere Strukturen eingebettet sind.


Beispiel

#include <string.h>

struct meine_strukt {

  char name[80], telefon_nummer[80];

  int  alter, groesse;

} mein_freund;

void func() {

strcpy(mein_freund.name,"Mr. Wizard"); /* Zugriff auf ein Element */

struct meine_strukt mein_freund[100];  /* Deklarieren weiterer Variablen */


switch (Schlüsselwort)
switch

Siehe auch	Beispiel	Schlüsselwörter

Syntax

switch ( <switch_variable> ) {

  case <konstantenausdruck> : <anweisung>; [break;]

    .

    .

    .

  default : <anweisung>;

}

Beschreibung

Die Anweisung switch übergibt die Ablaufkontrolle an dasjenige case-Label, das dem Wert der <switch_variable> entspricht, und es werden dann die dem case-Label folgenden Anweisungen ausgeführt.

Wenn kein case-Label die Bedingung erfüllt, so geht die Ablaufkontrolle zum Label default, und die darauf folgenden Anweisungen werden ausgeführt.

Um zu vermeiden, daß anschließend noch die Anweisungen anderer case-Zweige ausgeführt werden und um die Ausführung der switch-Anweisung zu beenden, schließt man jeden case-Zweig mit der Anweisung break;ab.


Beispiel

/* Im Folgenden eine Illustration des Gebrauchs der Schlüsselwörter
 break, case, default, und switch. */

#include <conio.h>

#include <stdio.h>

int main(void) {

   int ch;

   printf("\tDRÜCKEN SIE a, b, ODER c. JEDE ANDERE TASTE BEENDET

          "DIESES PROGRAMM.");

for ( /* ENDLOSSCHLEIFE */; ((ch = getch()) != EOF); )

      switch (ch) {

         case 'a' :    /* DIE WAHL VON a INITIIERT EINE EIGENE AKTION. */

            printf("\nOption a wurde gewählt.\n");

            break;

         case 'b' :    /* b UND c FÜHREN ZUM SELBEN RESULTAT. */

         case 'c' :

            printf("\nOption b oder c wurde gewählt.\n");

            break;

         default :

            printf("\nUNGÜLTIGE AUSWAHL!  Bye ...");

            return(-1);

         }

   return(0);

   }


this (Schlüsselwort)
this

Siehe auch	Schlüsselwörter

Syntax

class X {

int a;

public:

X (int b) {this -> a = b;}

Beschreibung

In nichtstatischen Elementfunktionen steht das Schlüsselwort this für einen Zeiger auf das Objekt, für das die Funktion aufgerufen wurde. Bei allen Aufrufen von nicht-statischen Elementfunktionen wird this als "verborgenes" Argument übergeben.

Das Schlüsselwort this repräsentiert eine lokale Variable, auf die im Körper jeder statischen Elementfunktion zugegriffen werden kann. Diese Variable wird innerhalb der Funktion implizit für Zugriffe auf Elemente benutzt. Sie muß nicht deklariert werden und wird in Funktionsdefinitionen nur selten explizit angegeben.

Beispiel: Im Aufruf x.funk(y), wobei y ein Element von X ist, erhält this den Wert &x, und y wird zu this->y, was gleichwertig mit x.y ist.

Statische Elementfunktionen haben keinen this-Zeiger, weil sie nicht im Hinblick auf ein bestimmtes Objekt aufgerufen werden. Eine statische Elementfunktion kann deshalb nicht auf nichtstatische Elemente zugreifen, ohne ein Objekt explizit mit . oder -> zu spezifizieren.


throw

Siehe auch	Schlüsselwörter

Syntax

throw zuweisungs_ausdruck

Beschreibung

Wenn eine Exception auftritt, initialisiert der throw-Ausdruck ein temporäres Objekt vom Typ T (passend zum Typ des Arguments arg), das in throw(T arg) benutzt wird. Weitere Kopien davon kann der Compiler je nach Notwendigkeit erzeugen. Deshalb kann es sinnvoll sein, einen Kopierkonstruktor für das Exception-Objekt zu definieren.


__try

Siehe auch	Schlüsselwörter

Syntax

__try  zusammengesetzte_anweisung  handler_liste

__try  zusammengesetzte_anweisung  abbruch_anweisung

Beschreibung

Das Schlüsselwort __try kann nur in C-Programmen verwendet werden; benutzen Sie in C++ Programmen statt dessen try.

Einem Programmstück, in dem eine Exception auftreten kann, muß das Schlüsselwort __try vorangestellt werden. Nach __try folgt eine in geschweifte Klammern eingeschlossene zusammengesetzte Anweisung. Damit wird festgelegt, daß das Programm in diesem Teil prüft, ob Exceptions auftreten. Wenn eine Exception auftritt, wird der normale Programmablauf unterbrochen, und das Programm beginnt, nach einem zu dieser Exception passenden Handler zu suchen. Wenn die Exception in einem C-Modul ausgelöst wurde, kann sie in einem C- oder in einem C++ Modul behandelt werden.

Wenn ein Handler für die erzeugte strukturierte Exception gefunden wird, können die folgenden Aktionen ausgeführt werden:

Ausführen der im Handler spezifizierten Aktionen

Ignorieren der erzeugten Exception und Fortsetzen der Programmausführung

Weitersuchen nach einem anderen Handler ("Erneuern" der Exception)

Wenn kein Handler gefunden wird, so ruft das Programm die Funktion terminate auf. Wenn keine Exceptions auftreten, läuft das Programm in der normalen Weise ab.


_ _try (Beispiel)
// try, Beispiel

// In PROG.C

void func(void) {

   // Eine Exception erzeugen

   RaiseException( /* Hier Ihre Argumente einsetzen */);

}

// In CALLER.CPP

// So testet man auf C++ oder C-basierte Exceptions.

#include <excpt.h>

#include <iostream.h>

int main(void) {

   try

   {            // Test auf C++ Exceptions

      try

      {         // Test auf C-basierte strukturierte Exceptions

         func();

      }

      __except( // Filterausdruck )

      {

      cout << "Es ist eine strukturierte Exception erzeugt worden.";

      /* Hier die durchzuführenden Aktionen für diese strukturierte */

      /* Exception spezifizieren. */

      return -1;

      }

      return 0;

   }

   catch ( ... )

   {

   // Handler für jede beliebige C++ Exception

   cout << "Es wurde eine C++-Exception erzeugt.";

   return 1;

   }

}


try

Siehe auch	Beispiel	Schlüsselwörter

Syntax

__try  zusammengesetzte_anweisung  handler_liste

Beschreibung

Das Schlüsselwort try kann nur in C++ Programmen verwendet werden; benutzen Sie in C-Programmen statt dessen __try.

Einem Programmstück, in dem eine Exception auftreten kann, muß das Schlüsselwort try vorangestellt werden. Nach try folgt eine in geschweifte Klammern eingeschlossene zusammengesetzte Anweisung. Damit wird festgelegt, daß das Programm in diesem Teil prüft, ob Exceptions auftreten. Wenn eine Exception auftritt, wird der normale Programmablauf unterbrochen und es werden folgende Schritte ausgeführt:

Das Programm sucht nach einem passenden Handler.

Wenn ein Handler gefunden wird, wird der Stack bis zu diesem Punkt "zurückgespult".

Die Programmkontrolle geht auf den Handler über.

Wenn kein Handler gefunden wird, ruft das Programm die Funktion terminate auf. Wenn keine Exceptions auftreten, läuft das Programm in der normalen Weise ab.


typedef (Schlüsselwort)
typedef

Beispiel	Schlüsselwörter

Syntax

typedef <typ_definition> <bezeichner> ;

Beschreibung

Das Schlüsselwort typedef dient dazu, der Datendefinition <typ_definition> den Namen <bezeichner> zuzuordnen.


Beispiele

typedef unsigned char byte;

typedef char str40[41];

typedef struct {

     double re, im;

   } complex;


typename

Syntax 1

typename <bezeichner>

Syntax 2

template <  typename <bezeichner>  > class <bezeichner>

Description

Verwenden Sie syntax 1, um einen Typ zu referenzieren, den Sie noch nicht definiert haben. Siehe Beispiel 1.

Verwenden Sie syntax 2 an Stelle eines Schlüsselwortes class in der Deklaration eines Templates. Siehe Beispiel 2.


typename Beispiel 2

/* Dieses Beispiel zeigt, wie das Schlüsselwort typename verwendet werden kann, um das Schlüsselwort class in der Deklaration eines Templates zu ersetzen. */

#include <iostream.h>

template <typename T1, typename T2> T2 convert (T1 t1)

                             // Verwenden Sie typename anstatt von class.

{ return (T2)t1; }

template <typename X, class Y> bool isequal (X x, Y y)

                             // typename und class gemischt.

{ if (x==y)return 1; return 0; }


typename Beispiel 1

/* Dieses Beispiel verwendet das Schlüsselwort typename, um Variablen als Type T::A zu deklarieren, was noch nicht definiert wurde. */

template <class T>

void f() {

  typedef typename T::A TA; 	// deklariert TA als Typ T::A

  TA a5;	          	// deklariert a5 als Typ TA

  typename T::A a6;         	// deklariert a6 als Typ T::A

  TA * pta6;             	// deklariert pta6 als Zeiger auf Typ TA

}


Operator typeid

Siehe auch	Beispiel	Schlüsselwörter

Syntax

typeid( ausdruck )

typeid( typ_name )

Beschreibung

Verwenden Sie das Schlüsselwort typeid, um zur Laufzeit Identifizierungsinformationen über Typen und Ausdrücke verfügbar zu machen. Der typeid-Aufruf liefert eine Referenz auf ein Objekt des Typs const typeinfo. zurück. Dieses Objekt repräsentiert den Typ des typeid-Operanden.

Wenn der Operand von typeid ein dereferenzierter Zeiger oder eine Referenz auf einen polymorphen Typ ist, so liefert typeid den dynamischen Typ des Objekts, auf das der Zeiger bzw. die Referenz im Moment weist, zurück. Wenn der Operand nicht polymorph ist, liefert typeid ein Objekt zurück, das den statischen Typ repräsentiert.

Der Operator typeid kann sowohl mit den eingebauten Basisdatentypen als auch mit benutzerdefinierten Datentypen zusammen benutzt werden.

Wenn der Operand typeid ein Delphi Klassenobjekt/-referenz ist, liefert typeid den statischen anstelle des Laufzeit-Typs. Klicken Sie hier, um ein Beispiel zu sehen.

Wenn der Operand von typeid ein dereferenzierter Nullzeiger ist, so wird die Exception Bad_typeid ausgelöst.


Delphi typeid (Beispiel)
Delphi typeid Beispiel

// Bei Delphi-Objekten liefert typeid den statischen Typen nicht den Laufzeit-Typen

static const char *TypeIdName(TObject *c) {

   return typeid(*c).name();

}

//  Die Beschriftung der Schaltfläche wurde auf TObject gesetzt, nicht TButton.

void __fastcall TForm1::Button1Click(TObject *Sender) {

   Button1->Caption = TypeIdName(Button1);

}


typeid (Beispiel)
// typeid Beispiel

// VERWENDUNG DER OPERATOREN typeid, Type_info::before() UND
 Type_info::name()

#include <iostream.h>

#include <typeinfo.h>

class A { };

class B : A { };

void main() {

   char C;

   float X;

// VERWENDEN SIE FÜR VERGLEICHE typeinfo::operator==()

   if (typeid( C ) == typeid( X ))

      cout << "C und X sind vom selben Typ." << endl;

   else cout << "C und X sind NICHT vom selben Typ." << endl;

// VERWENDEN SIE FÜR VERGLEICHE DIE KONSTANTEN true UND false

   cout << typeid(int).name();

   cout << " vor " << typeid(double).name() << ": " <<

        (typeid(int).before(typeid(double)) ? true : false) << endl;

   cout << typeid(double).name();

   cout << " vor " << typeid(int).name() << ": " <<

        (typeid(double).before(typeid(int)) ? true : false) << endl;

   cout << typeid(A).name();

   cout << " vor " << typeid(B).name() << ": " <<

        (typeid(A).before(typeid(B)) ? true : false) << endl;

   }

Ausgabe des Programms:

C und X sind NICHT vom selben Typ.

int vor double: 0

double vor int: 1

A vor B: 1


union (Schlüsselwort)
union

Siehe auch	Beispiel	Schlüsselwörter

Syntax

union [<union_typ_name>] {

  <typ> <variablen_namen> ;

  ...

} [<union-variablen>] ;

Beschreibung

Eine Variante (union) ermöglicht es, Variablen zu definieren, die sich denselben Speicherbereich teilen.

Der Compiler reserviert genügend Speicherplatz für das größte Element der Variante eine_zahl, vgl das Beispiel.

Im Gegensatz zu einer Struktur werden die Variablen eine_zahl.i und eine_zahl.l am gleichen Speicherplatz gespeichert. Das Speichern der einen überschreibt also die andere.

Zum Zugriff auf einzelne Elemente einer Variante dient der Elementselektor (.).


Beispiel

union int_oder_long {

  int     i;

  long    l;

} eine_zahl;


unsigned (Typ-Modifizierer)
unsigned

Siehe auch	Beispiel	Schlüsselwörter

Syntax

unsigned <typ> <variable> ;

Beschreibung

Der Typmodifizierer unsigned wird verwendet, wenn der Wert einer Variablen stets positiv ist. Dieser Modifizierer kann auf die Basistypen int, char, long und short angewendet werden.

Fehlt in einer Deklaration die Angabe des Basistyps, so wird dafür int angenommen.


Beispiele

unsigned int       i;

unsigned           i;    /* Gleichwertig mit "unsigned int i;"    */

unsigned long int  l;    /* "int" ist erlaubt, aber nicht erforderlich */

unsigned char      ch;   /* Voreinstellung für char ist unsigned */


virtual (Schlüsselwort)
virtual

Siehe auch	Schlüsselwörter

Syntax

virtual klassenname

virtual funktionsname

Beschreibung

Das Schlüsselwort virtual ermöglicht es abgeleiteten Klassen, unterschiedliche Versionen einer Funktion der Basisklasse bereitzustellen. Wenn Sie eine Funktion einmal als virtual deklariert haben, können Sie diese in jeder abgeleiteten Klasse neu definieren, und zwar sogar dann, wenn Anzahl und Typen der Argumente gleich bleiben.

Eine neudefinierte Funktion überdeckt die Funktion der Basisklasse.


void

Beispiel	Schlüsselwörter

Syntax

void bezeichner

Beschreibung

Das Schlüsselwort void bezeichnet den Typ des Rückgabewerts von Funktionen, die keinen Wert zurückliefern.

void hallo(char *name)

{

  printf("Hallo, %s.",name);

}

Die Angabe void in der Parameterliste einer Funktion bedeutet, daß die Funktion keine Parameter hat.

int init(void)

{

  return 1;

}

void-Zeiger:

Es können auch Zeiger vom Typ "Zeiger auf void" deklariert werden.

void-Zeiger können nicht ohne explizite Typumwandlung dereferenziert werden, weil der Compiler sonst die Größe des Objekts, auf das der Zeiger zeigt, nicht erkennen kann.


Beispiel

int x;

float r;

void *p = &x;          /* p zeigt auf x */

int main (void)

  *(int *) p = 2;

  p = &r;              /* p zeigt auf r */

  *(float *)p = 1.1;

}


volatile (Schlüsselwort)
volatile

Siehe auch	Schlüsselwörter

Syntax

volatile <datendefinition> ;

Beschreibung

Der Modifizierer volatile teilt dem Compiler mit, daß eine Variable möglicherweise von einer Hintergrundroutine, einer Interruptroutine oder einem I/O-Port verändert wird. Die Deklaration eines Objektes als volatile warnt den Compiler, keine den Wert des Objektes betreffenden Zusicherungen zu machen, so lange die Ausdrücke, in denen es erscheint, evaluiert werden. Denn der Wert könnte sich jeden Augenblick ändern. Es hält den Compiler auch davon ab, aus einer Variablen eine Registervariable zu machen.

volatile int ticks;

void timer( ) {

   ticks++;

}

void wait (int interval) {

   ticks = 0;

   while (ticks < interval);  // Nichts tun

}

Die Routinen in diesem Beispiel (sicherstellend, daß timer passend mit einem Interrupt der Hardware-Uhr verbunden ist) implementieren ein zeitliches Warten auf Schläge, die durch das Argument interval spezifiziert sind. Ein besonders optimierter Compiler könnte den Wert von ticks innerhalb des Tests der Schleife while nicht laden, seit die Schleife den Wert von ticks nicht ändert.

Hinweis:	C++ erweitert volatile auf Klassen und Elementfunktionen. Wenn Sie ein Objekt volatile deklariert haben, können Sie ausschließlich seine volatilen Elementfunktionen verwenden.


while (Schlüsselwort)
while

Siehe auch	Beispiel	Schlüsselwörter

Syntax

while ( <bedingung> ) <anweisung>

Beschreibung

Das Schlüsselwort while dient zur bedingten wiederholten Ausführung einer Anweisung.

Die <anweisung> wird so lange wiederholt ausgeführt, bis der Wert von <bedingung> false ist. Falls keine Bedingung angegeben ist, wird while(true) angenommen.

Die Prüfung findet statt, bevor <anweisung> ausgeführt wird. Deshalb wird die Schleife keinmal durchlaufen, falls <bedingung> zu Anfang des ersten Durchlaufs den Wert false ergibt.


Beispiel

while (*p == ' ') p++;


wchar_t (Schlüsselwort)

Syntax

wchar_t <bezeichner>;

Beschreibung

In C++ Programmen ist wchar_t ein Basisdatentyp, der die Codes aller Zeichen, auch der größten erweiterten Zeichnsätze aller unterstützten nationalen Versionen (locale) ohne Informationsverlust aufnehmen kann. Der Typ wchar_t ist, was Speicherplatzbedarf, Vorzeicheneigenschaft und Ausrichtungserfordernis angeht, identisch mit dem Typ int.


C++ Sprachunterstützung für die VCL

Siehe auch

Datentypen

Eigenschaften

Closures

OLE-Automatisierung

Open Arrays

Exception-Behandlung

Wertebereichsgrenzen

C++Builder unterstützt die Standard-Borland C++ 5.01-Syntax für 32-Bit-Anwendungen. Beim Aufbau und bei der Ausführung von Anwendungen ist C++Builder dagegen stark von der Delphi-Bibliothek visueller Komponenten (VCL) abhängig, die auf Object Pascal basiert. Es gibt daher einige notwendige Ergänzungen und Detailunterschiede in der C++ Implementierung, die Sie als Programmierer kennen müssen.

Alle Referenzen auf VCL-Objekte und davon abgeleitete Objekte erfolgen über Zeiger. Normalerweise wird dabei der Elementzugriffsoperator (->) verwendet. Das folgende ist eine typische Ereignisbehandlungsroutine in Delphi:

procedure TForm1.Button1Click(Sender: TObject);

begin

  Listbox1.Items.Add(Edit1.Text);

  Edit1.Text := ‘’;

end;

In C++Builder hat die Routine dann diese Form:

void __fastcall TForm1::Button1Click(TObject *Sender)

{

  Listbox1->Items->Add(Edit1->Text);

  Edit1->Text = „“;

}

Die VCL basiert auf der Aufrufkonvention __fastcall, bei der Funktionsparameter und Rückgabewerte nicht über den Speicher, sondern in Registern übergeben werden. Die IDE fügt bei allen generierten Funktionen und Ereignisbehandlungsroutinen den Modifizierer __fastcall in den Code ein.


Unterstützung für Delphi-Datentypen

Siehe auch	Tabelle der Delphi-Datentypen

C++Builder unterstützt die meisten intrinsischen Delphi-Datentypen, entweder, indem der Delphi-Typ mit typedef in einen Standard-C++ Typ umgewandelt wird, oder indem mit Hilfe einer Klasse die Funktionalität des Typs emuliert wird. Die folgende Tabelle enthält die Delphi-Datentypen und deren C++Builder-Implementationen.

Die meisten intrinsischen Delphi-Typen existieren auch in C++Builder, weshalb Sie bei der Deklarationssyntax freie Wahl haben. Beispielsweise sind diese Zeile:

Single pi = 3.14159;

und diese Zeile :

float pi = 3.14159;

gültige C++Builder-Syntax und äquivalent.

Hinweis:	In C++Builder wird, anders als bei Delphi, zwischen Groß- und Kleinschreibung unterschieden. So sind etwa Single und single nicht dasselbe. Die korrekten Schreibweisen der Delphi-Typen finden Sie in INCLUDE\VCL\SYSDEFS.H.


Tabelle der Delphi-Datentypen

Delphi	Größe/Werte	C++ Implementierung	Implementierung

ShortInt	8-Bit-Integer	char	typedef
SmallInt	16-Bit-Integer	short	typedef
LongInt	32-Bit-Integer	long	typedef
Byte	8-Bit-Integer ohne Vorzeichen	unsigned char	typedef
Word	16-Bit-Integer ohne Vorzeichen	unsigned short	typedef
Integer	32-Bit-Integer	int	typedef
Cardinal	32-Bit-Integer ohne Vorzeichen	unsigned long	typedef
Boolean	true/false	bool	typedef
ByteBool	true/false oder 8-Bit-Integer ohne Vorzeichen	unsigned char	typedef
WordBool	true/false oder 16-Bit-Integer ohne Vorzeichen	unsigned short	typedef
LongBool	true/false oder 32-Bit-Integer ohne Vorzeichen	unsigned long	typedef
AnsiChar	8-Bit-Zeichen ohne Vorzeichen	unsigned char	typedef
WideChar	16-Bit-Unicode-Zeichen	wchar_t	typedef
Char	8-Bit-Zeichen ohne Vorzeichen	char	typedef
AnsiString	Delphi-AnsiString	AnsiString	Klasse
String[n]	bisheriger Delphi-String, n = 1..255 Byte	SmallString<n>	Vorlagenklasse
ShortString	bisheriger Delphi-String, 255 Byte	SmallString<255>	typedef
String	Delphi-AnsiString	AnsiString	typedef
Single	32-Bit-Gleitkommazahl	float	typedef
Double	64-Bit-Gleitkommazahl	double	typedef
Extended	80-Bit-Gleitkommazahl	long double	typedef
Currency	64-Bit-Gleitkommazahl, 4 Dezimalstellen	Currency	Klasse
Real	32-Bit-Gleitkommazahl	float	typedef
Comp	64-Bit-Gleitkommazahl	double	typedef
Set	1..32 Byte	Set<type, minval, maxval>	Vorlagenklasse
Pointer	generischer 32-Bit-Zeiger	void *	typedef
PChar	32-Bit-Zeiger auf Zeichen	unsigned char *	typedef
PAnsiChar	32-Bit-Zeiger auf ANSI-Zeichen	unsigned char *	typedef
Variant	OLE-Variante (16 Byte)	Variant	Klasse


Spezielle Delphi-Parametertypen

Siehe auch

Einige Delphi-Funktionen und Prozeduren nehmen Parametertypen entgegen, auf die besonders hingewiesen werden soll. Folgende Typen sind gemeint:

Open Arrays	Ein Array, dessen Elementzahl nicht explizit feststeht.

Var-Parameter	Ein veränderliches Argument.

Untypisierte Parameter	Ein Parameter ohne spezielle Typfestlegung.

Open Arrays

Für ein Open Array gilt in Delphi folgende Syntax:

array of <type>

Ein Beispiel:

Procedure MyFunc(x : array of integer);  // Aufruf per Wert; keine Größe angegeben.

In C++ gibt es keinen intrinsischen Typ, der dem Open Array in Pascal entspricht. Allerdings kann dessen Funktionalität nachgebildet werden. Wenn ein array of T als Parameter übergeben wird, erfolgt intern eine Zerlegung in einen Zeiger auf das Array selbst und in einen long-Wert, der den größten Elementindex enthält (d.h. die Anzahl der Array-Elemente minus eins). Damit erhält der Funktionsprototyp in C++ folgende Form:

void MyFunc(int *x, long n);

Hier ist x ein Zeiger auf das Integer-Array, und n bezeichnet den größten Elementindex.

Beispielsweise nimmt die arithmetische VCL-Funktion Mean() einen Parameter entgegen, ein array of Double:

function Mean(const Data: array of Double): Extended;

Sehen Sie nun, wie die Funktion in C++ aufgerufen wird:

double d[4];

double *pd = &d;

d[0] =  3.14;

d[1] = 34.33;

d[2] =  9.34;

d[3] = 64.95;

// Aufruf per Übergabe eines Zeigers auf das Array und des höchsten Elementindex

long double x = Mean(pd, 3L);

var-Parameter

Funktionen, wie die folgende, die in Pascal einen var-Parameter erhalten (einen veränderlichen Parameter):

procedure myFunc(var x : int);

sollten mit der üblichen C++ Syntax Übergabe per Referenz aufgerufen werden:

void myFunc(int& x);

Untypisierte Parameter

In Pascal können Parameter übergeben werden, ohne daß ein Typ dafür definiert wurde. Die empfangende Funktion muß den Parameter dann vor der Verwendung erst in einen bekannten Typ umwandeln. C++Builder akzeptiert untypisierte Parameter als Zeiger auf void (void *). Auch hier muß die empfangende Funktion den Zeiger in den gewünschten Typ umwandeln. Ein Beispiel:

int myfunc(void *MyName)

{

// Zeiger in korrekten Typ umwandeln, dann dereferenzieren.

return 1 + (int *)MyName;

}


Eigenschaften

Siehe auch	 Beispiele

Das Schlüsselwort __property verknüpft einen bestimmten Lese-/Schreibzugriff mit einem Bezeichner.

Ausschließlich Klassen können Eigenschaften besitzen. Aus der Sicht des Benutzers verhalten sich Eigenschaften wie Datenelemente, kapseln aber intern Elementfunktionen, mit denen das Datenelement gelesen und geschrieben werden kann. Eine Eigenschaftsdefinition in einer Klasse deklariert ein benanntes Attribut für Objekte dieser Klasse und deklariert Aktionen, die mit dem Lesen und Schreiben des Attributs verbunden sind. Beispiele für Eigenschaften sind der Titel eines Formulars, die Größe einer Schrift oder der Name einer Datenbanktabelle.

Mit Hilfe von Eigenschaften lassen sich dynamische Seiteneffekte erzielen, während es der Benutzer augenscheinlich nur mit einem einfachen Datenelement zu tun hat. Wenn in der C++Builder-Umgebung beispielsweise die Eigenschaft Caption eines Formulars geändert wird (scheinbar nur ein Datenelement des Formularobjekts), wird sofort der Titel des Formularfensters angepaßt, ohne daß der Benutzer dazu explizit eine Elementfunktion aufrufen muß.

Eigenschaften können einen beliebigen Typ besitzen, dürfen aber keine Datei sein.

Eigenschaften sind die konzeptionelle Erweiterung von Datenelementen. Mit beiden lassen sich die Attribute eines Objekts beschreiben, wobei Datenelemente in erster Linie als Speicherstellen fungieren, die beliebig abgefragt und geändert werden können. Eigenschaften bieten dagegen eine umfassendere Steuerung des Zugriffs auf die Attribute, denn sie bieten einen Mechanismus dafür, die einfachen Lese- und Schreibzugriffe auf die Attribute mit weitergehenden Aktionen zu verknüpfen und ermöglichen auch, die Attribute zu berechnen.

In VCL-basierten Klassen definieren Eigenschaften außerdem die Persistenz einer Klasse, in der sie gespeichert und geladen werden, wenn ein Objekt dieser Klasse persistent ist.

Bei Array-Eigenschaften kann der Index auf die Arrays (<prop dim list>) einen beliebigen Typ haben.

Eigenschaften können nur in Klassen deklariert werden.

Der Bezeichner in einer Schreiben- oder Lesen-Klausel muß ein Datenelement oder eine Elementfunktion sein.


Eigenschaftsattribute

Siehe auch

Syntax

stored = <expression>

stored = <data member or member function>

default = <constant expression>

nodefault

index = <constant expression>

read = <data or member function>

write = <data or member function>

__dispid(constant int expression)

Beschreibung

 stored = <expression> Der Wert des Ausdrucks legt fest, ob diese Eigenschaft in der zugehörigen Formulardatei (DFM) gespeichert werden soll. Wenn expression den Wert True hat, wird die Eigenschaft gespeichert.

default = <constant expression> Dies gilt für Eigenschaften mit ordinalem Typ, weshalb <constant> auch einen ordinalen Typ besitzen muß. Ein Eigenschaftswert wird nur dann in einer Formulardatei gespeichert, wenn sich der aktuelle Wert von der Vorgabe unterscheidet oder wenn es keinen Vorgabewert gibt.

nodefault Dies wird von Eigenschaften verwendet, um eine vorangegangene Zuweisung default= Wert zu überschreiben.

index = <constant expression> Dieser Index wird eingesetzt, wenn eine einzige Abruf- oder Zuweisungsroutine für mehrere Eigenschaften verwendet wird. Die Indexkonstante ist ein nicht negativer Integer, der als zusätzlicher Parameter an eine Lese-/Schreib-Elementfunktion übergeben werden kann.

write = <data or member function> Das Zugriffsattribut einer Eigenschaft mit einem Indexattribut muß den Namen einer Elementfunktion bezeichnen. Anders ausgedrückt, die Attribute read und write einer Eigenschaft dürfen kein Datenelement bezeichnen.

Beim Zugriff auf eine Eigenschaft mit einem Indexattribut wird der in der Eigenschaftsdefinition festgelegte Integer als zusätzlicher Parameter an die Zugriffsfunktion übergeben. Deshalb muß diese Funktion einen zusätzlichen Werteparameter des Typs int akzeptieren.

Der Index wird immer als letzter Parameter übergeben. Ein Beispiel dazu finden Sie unter Indizierte Eigenschaften.

__dispid(constant int expression) Eine Elementfunktion, die im __automated-Abschnitt einer Klasse deklariert wurde, kann eine optionale __dispid(constant int expression)-Direktive enthalten. Diese Direktive muß nach der schließenden Klammer der Parameterliste deklariert werden.


Indizierte Eigenschaften (Beispiel)

struct MyObject

{

   // Diese Funktion wird auf zwei Eigenschaften angewendet.

   // Deshalb wird über den Index zwischen Left und Right unterschieden.

   void Setter(double, int);

   __property double Left =

                   {write = Setter,

                    index = 0};

   __property double Right =

                    {write = Setter,

                     index = 1};

};


Eigenschaftsoperatoren

Siehe auch

Arithmetische Typen

Eigenschaften mit arithmetischem Typ benutzen compiler-definierte Operatoren. Werte von Eigenschaften können mit den üblichen Operatoren für Zuweisung, Inkrementierung und Dekrementierung geändert werden. Wenn eine Abruffunktion definiert ist, wird stets diese verwendet. Folgendes Beispiel ist korrekt:

property int i;

i++;

Eine Inkrementierung erfolgt so:

i += 1;

Klassentypen

C++Builder bietet alle notwendigen Operatoren für Delphi-Datentypen, die als Klassentypen implementiert sind.

Überladene Operatoren für Eigenschaften solcher Klassentypen erfordern eine temporäre Kopie dieser Klasse. Wenn Sie einen überladenen Operator vorsehen, wird er nur auf die temporäre Klasse angewendet. Sie müssen trotzdem den vordefinierten Zuweisungsoperator verwenden, der in C++Builder für diesen Klassentyp implementiert ist.

Wenn beispielsweise der Einfügeoperator für eine Eigenschaft überladen wurde, muß er folgendermaßen verwendet werden:

prop << 3;           // Aktualisiert nur die temporäre Kopie.

prop = prop << 3;    // Aktualisiert das Original.

Abruf- und Zuweisungsfunktionen

operator +=( ) wird für Eigenschaften mit Abruf- und Zuweisungsfunktionen nicht unterstützt.


Übernommene Eigenschaften

Siehe auch	Beispiele

Syntax

__property <identifier> = { <attrib list> };

Beschreibung

Der Ausdruck übernommene Eigenschaft bezeichnet eine Eigenschaft, die in einer Basisklasse definiert und in einer abgeleiteten Klasse verändert wurde. Mögliche Änderungen umfassen die Zugriffsrechte der Eigenschaften oder bestimmte Attribute der aus der Basisklasse stammenden Eigenschaft.


Übernommene Eigenschaften (Beispiel)

struct MyObject

{

   int Getter(void);

   bool IsStored(void);

__published:

   __property int myProperty = {

                     read = Getter,

                     stored = IsStored,

                     default = 42 };

};

struct MyDerivedObject : MyObject

{

__published:

   __property myProperty = { nodefault };

};


Eigenschaftsdeklarationen

class myClass (

int data1;

int GetCounter();

void SetCounter(int);

public:

myClass() {data1 = 0;};

__property int counter = {read=GetCounter, write=SetCounter};

};

Diese Klasse deklariert eine Integer-Eigenschaft namens counter. Jedesmal, wenn der Wert von counter gelesen wird, erfolgt ein Aufruf der Funktion GetCounter. Deshalb sind die beiden folgenden Anweisungen funktional gleichwertig:

int x = counter;

int x = GetCounter();

Wenn der Eigenschaft counter dagegen ein Wert zugewiesen wird, erfolgt ein Aufruf der Funktion  SetCounter. Deshalb sind auch diese beiden Anweisungen gleichwertig:

counter = 100;

SetCounter(100);

Indem Eigenschaften verwendet werden, lassen sich mit dem einfachen Vorgang des Lesens oder Schreibens weitere Aktionen verbinden, etwa eine Gültigkeitsprüfung, Fehlerbehandlung oder sogar die Änderung von Werten. Die Wertzuweisung an eine Eigenschaft kann eine weitere Operation auslösen, die über einfaches Speichern hinausgeht.

Eigenschaften können auch mit Datenelementen verknüpft werden, nicht nur mit Funktionen. Angenommen, die Eigenschaft aus dem obigen Beispiel würde folgendermaßen deklariert:

__property int counter = {read=data1, write=data1};

Dann würde die Anweisung

int x = counter;

den Wert von data1 an x übergeben. Dieses Verhalten könnte beispielsweise dieses Konstrukt ermöglichen:

counter = 10;	// weist func1 den Wert 10 zu

data1 = 20;

int y = counter;	// y erhält den Wert 20


Array-Eigenschaften

Siehe auch	Beispiele

Syntax

__property <type> <id> [<type>] = { <attrib list> };

Beschreibung

Sie können Eigenschaften deklarieren, die in Form und Verhalten sehr einem Array ähneln. Solche Eigenschaften besitzen mehrere Werte desselben Typs, die über einen Index referenziert werden. Anders als bei einem Array, kann die Eigenschaft nicht als ganzes referenziert werden, sondern immer nur einzelne Elemente davon. Eigenschaften dieser Art werden als Array-Eigenschaften bezeichnet.

Im folgenden werden dazu zwei wichtige Aspekte behandelt:

Array-Eigenschaften deklarieren

Auf Array-Eigenschaften zugreifen

Array-Eigenschaften deklarieren

Die Deklaration einer Array-Eigenschaft gleicht der Deklaration einer beliebigen anderen Eigenschaft. Allerdings kommt eine Indexparameter-Liste hinzu, die Name und Typ der Indizes auf die Array-Eigenschaft festlegt. Ein Beispiel:

__property int widget[int index] = {read=GetWidget, write=SetWidget};

Eine solche Liste hat dasselbe Format wie eine Liste formaler Funktionsparameter. Im Gegensatz zu Array-Typen, deren Dimension als Integer festgelegt werden muß, sind bei Array-Eigenschaften Indizes mit beliebigem Typ möglich.

Auf Array-Eigenschaften zugreifen

Der Zugriff auf eine Array-Eigenschaft erfolgt über den Eigenschaftsbezeichner, dem eine Liste aktueller Parameter nachgestellt ist, die in eckige Klammern eingeschlossen sind. Array-Eigenschaften können nicht als ganzes angesprochen werden.

Eigenschaften mit Mehrfachindex

Ebenso wie ein gewöhnliches Array kann auch eine Array-Eigenschaft mehr als einen Index besitzen. Die zugehörigen Parameter der Lese- und Schreibfunktionen müssen lediglich dieselben Signaturen wie die Indizes besitzen und in derselben Reihenfolge wie die Indizes stehen.

Das Zugriffsattribut einer Array-Eigenschaft muß eine Elementfunktion bezeichnen. Anders ausgedrückt, die Lese- und Schreibattribute einer Array-Eigenschaft dürfen sich nicht auf ein Datenelement beziehen.

Die Elementfunktionen, die im Zugriffsattribut einer Array-Eigenschaft aufgeführt sind, müssen folgenden Regeln entsprechen:

Das Attribut read einer Array-Eigenschaft muß sich auf eine Funktion beziehen, welche dieselbe Anzahl und denselben Typ von Parametern akzeptiert, wie in der Indexparameter-Liste der Eigenschaft angegeben. Der Typ des Funktionsrückgabewertes muß mit dem Typ der Eigenschaft übereinstimmen.

Das Attribut write einer Array-Eigenschaft muß sich auf eine Funktion beziehen, welche dieselbe Anzahl und denselben Typ von Parametern akzeptiert, wie in der Index-Parameterliste der Eigenschaft angegeben, sowie einen zusätzlichen Parameter mit demselben Typ wie die Eigenschaft.


Array-Eigenschaften (Beispiel)

struct MyObject

{

int Getter(char *, int);

   void Setter(char *, int, int);

   __property int prop[char*][int] = { read = Getter, write = Setter);

};

int myFunc(MyObject * optr)

{

   return optr -> prop[„C++Builder“][1996];

}


Closures

Siehe auch	Beispiele

Mit dem Schlüsselwort __closure wird ein Zeiger auf eine Elementfunktion deklariert. Solche Zeiger werden als Closures bezeichnet.

Ein Closure ordnet einen Zeiger auf eine Elementfunktion einem Zeiger auf eine Klasseninstanz zu. Der Zeiger auf die Klasseninstanz wird beim Aufruf der zugehörigen Elementfunktion als this-Zeiger verwendet.

Die Deklaration gleicht der eines Funktionszeigers, es wird lediglich das Schlüsselwort __closure hinzugefügt, bevor der Bezeichner definiert wird. Während ein Funktionszeiger nur eine 4-Byte-Code-Adresse enthält, enthält ein Closure sowohl diese Code-Adresse (in den ersten vier Byte) als auch einen Objektzeiger (in den nächsten vier Byte), der als this-Zeiger fungiert, wenn der Aufruf durch den Closure erfolgt.


Zugriffsattribute

Siehe auch

Als Ergänzung der normalen C++ Zugriffsattribute können Sie nun den Bereich innerhalb einer Klasse definieren, der im Objektinspektor sichtbar (published) oder einem OLE-Automatisierungs-Server zugänglich sein soll. Hierzu gibt es die folgenden Themen:

Published-Eigenschaften

OLE-Automatisierung


Published-Eigenschaften

Siehe auch

Mit Hilfe des Schlüsselworts __published können Eigenschaften so gekennzeichnet werden, daß sie im Objektinspektor sichtbar sind, wenn es sich bei der Klasse um eine Komponente handelt, die zur Komponentenpalette hinzugefügt wird. Es können nur solche Klassen __published-Abschnitte besitzen, die von TObject abgeleitet sind.

Jede Eigenschaft, die so deklariert ist, muß die Aufrufkonvention __fastcall für jede ihrer Methoden zum Lesen, Schreiben und Speichern verwenden.

Die Sichtbarkeitsregeln für als published deklarierte Elemente sind mit denen für als public deklarierte Elemente identisch. Der einzige Unterschied zwischen beiden liegt darin, daß Laufzeit-Typinformationen (RTTI = Run Time Type Information) im Stil von Delphi nur für die Datenelemente und Eigenschaften erzeugt werden, die in einem __published-Abschnitt stehen. Die RTTI ermöglicht es der Anwendung, die Datenelemente, Elementfunktionen und Eigenschaften eines andernfalls unbekannten Klassentyps abzufragen.

In einem __published-Abschnitt sind keine Konstruktoren und Destruktoren erlaubt. Dagegen sind intrinsische Pascal-Datenelemente, von der VCL abgeleitete Datenelemente, Elementfunktionen und Closures in einem published-Abschnitt zulässig.


OLE-Automatisierung

Siehe auch	Beispiele

Die Basisklasse für alle Objekte in Automatisierungs-Servern ist TAutoObject.

Die Sichtbarkeitsregeln für Elemente, die als automated deklariert sind, stimmen mit denen für die Elemente überein, die als public deklariert sind. Der einzige Unterschied liegt darin, daß für als automated deklarierte Elementfunktionen und Eigenschaften entsprechende Informationen generiert werden, mit deren Hilfe OLE-Automatisierungs-Server erzeugt werden können.

Bei einer Elementfunktion müssen alle Parameter sowie der Rückgabewert (falls vorhanden) automatisierbar sein. Ähnliches gilt für Eigenschaften. Auch hier müssen der Eigenschaftstyp und die Typen aller Array-Parameter automatisierbar sein, das heißt als automated deklariert werden können. Es führt zu einem Fehler, wenn Elementfunktionen oder -Eigenschaften deklariert werden, die in einem __automated-Abschnitt ungeeignete Typen enthalten.

Folgende Typen können als automated deklariert werden:

Currency

double

int

float

short

String

TDateTime

Variant

unsigned short

Klassen sollten von TAutoObject abgeleitet werden.

In Deklarationen von Elementfunktionen muß die Aufrufkonvention __fastcall verwendet werden.

Elementfunktionen können virtuell sein (virtual).

Elementfunktionen können die Anweisung __dispid(constant int expression) nach der schließenden Klammer der Parameterliste beinhalten.

Eigenschaftsdeklarationen können Zugriffsattribute umfassen (dispid, read und write). Andere Attribute sind nicht erlaubt (index, stored, default, nodefault).

Zugriffsattribute für Eigenschaften müssen auf den Bezeichner einer Elementfunktion verweisen. Bezeichner für Datenelemente sind nicht erlaubt.

Elementfunktionen für den Zugriff auf Eigenschaften müssen die Aufrufkonvention __fastcall verwenden.

Das Überschreiben von Eigenschaften (Eigenschaftsdeklarationen ohne Typangabe) ist nicht erlaubt.


Open Arrays

Siehe auch

Temporäre Arrays als Argumente

Vorhandene Arrays als Argumente

Einige VCL-Funktionen akzeptieren Open Arrays als Argumente. Zur Übergabe wird eines der folgenden Makros verwendet: EXISTINGARRAY, SLICE oder OPENARRAY.

Die Übergabe eines Open Arrays als Argument kann auf eine der folgenden Arten erfolgen:

var	Per Referenz, Argumente können geändert werden.

per Wert	Die aufrufende Funktion kopiert die Argumente. Die Kopie kann geändert werden.

Wichtiger Hinweis:  Wenn Sie eine C++ Funktion schreiben, die ein Open Array eines ihrer Argumente per Wert übernimmt, muß die Funktion dafür sorgen, daß eine Kopie des Open Arrays erzeugt wird. Der Compiler regelt dies NICHT automatisch.

array of const	Argumente können nicht geändert werden. Immer ein Array von TVarRec.

Die Funktionstypen, die Array-Argumente entgegennehmen, haben folgende Form:

// Pascal-Funktion; Aufruf per Wert, Argumente werden nicht verändert

function sum(array of double): double;

// C++ Äquivalent der obigen Funktion; const-Argument wird nicht verändert

double sum(const double* nums, int sizeless1);

// Diese Pascal-Prozedur kann ihr Argument verändern

procedure zero(var array of double);

// C++ Äquivalent, Argument kann verändert werden

void zero(double* nums, int sizeless1);


Temporäre Arrays als Argumente

Siehe auch 	Beispiele

Temporäre Arrays als Argumente werden für Delphi-Funktionen und Prozeduren benötigt, die ein array of T akzeptieren, wobei es aber kein Argument gibt, das die Elementanzahl bezeichnet. Wenn solche Delphi-Routinen aufgerufen werden, können mit dem Makro OPENARRAY temporäre Open Arrays aufgebaut werden. Das Makro ist in der Header-Datei INCLUDE\VCL\SYSDEFS.H definiert. Die Deklaration lautet folgendermaßen:

// <T> ist der von der Funktion erwartete Datentyp

OPENARRAY(<T>, (value1, value2, value3)) // bis zu 19 Werte

Das Makro OPENARRAY erzeugt ein temporäres Array des angegebenen Typs und initialisiert es mit den übergebenen Werten. Maximal 19 Werte sind möglich. Wenn mehr Werte benötigt werden, müssen Sie selbst ein Array aufbauen.

Das Makro OPENARRAY garantiert außerdem die korrekte Löschung des Arrays nach Gebrauch.

Beachten Sie folgende Regeln, wenn Sie temporäre Arrays als Funktionsargumente verwenden.

Temporäre Arrays können nicht an ein var-Open Array übergeben werden.

Temporäre Arrays können per Wert an eine Funktion mit einem Open Array-Argument übergeben werden. Die aufrufende Funktion muß eine Kopie des temporären Open Arrays erzeugen. Die Kopie kann verändert werden.

// Das Makro OPENARRAY erzeugt ein temporäres Integer-Array.

OPENARRAY(int, (5, 7, 82, myIntVariable, 96))

Temporäre Arrays können als Funktionsargument übergeben werden, wenn der erwartete Typ array of const ist. Das temporäre Array wird nicht geändert.

// Der Typ muß immer TVarRec sein

OPENARRAY(TVarRec, (myString, fontName, 34, 22.0, myVariant, "abc", 'd'));


Vorhandene Arrays als Argumente

Siehe auch

Wenn eine VCL-Funktion ein Array erfordert, können Sie ein Array des passenden Typs verwenden, das Sie definiert haben. Mit dem Makro EXISTINGARRAY übergeben Sie ein solches Array an die aufrufende Funktion. Das Makro EXISTINGARRAY liefert der aufrufenden Funktion den Typ und die Größe des Arrays.

Beachten Sie die folgenden Regeln, wenn Sie ein vorhandenes Array als Funktionsargument übergeben:

Vorhandene Arrays können an Funktionen übergeben werden, die ein Open Array als var-Parameter erfordern. Das vorhandene Array wird übergeben und kann verändert werden.

EXISTINGARRAY(myExistingArrayOfTheCorrectType)

Vorhandene Arrays können an Funktionen übergeben werden, die ein Open Array als Werteparameter erfordern. Die aufrufende Funktion erzeugt eine Kopie des Arrays. Die Kopie kann verändert werden.

EXISTINGARRAY(myExistingArrayOfTheCorrectType)

Vorhandene Arrays können an Funktionen übergeben werden, die als Argument ein array of const erfordern. Obwohl dies technisch möglich ist, ist es unwahrscheinlich, daß bereits ein TVarRec-Array existiert. Arrays dieser Art sollten vermieden werden, weil sie den Referenzzähler nicht exakt verwalten, das heißt es kann Zeiger geben, die auf nichts verweisen.

// Kein Beispiel für die Praxis

EXISTINGARRAY(myExistingArrayOfTVarRecType)


Behandlung von Delphi-Exceptions

Siehe auch

Betriebssystem-Exceptions

Delphi-Exceptions

Aspekte der Portierbarkeit

Die Implementierung der Exception-Behandlung in C++Builder unterstützt sowohl den Object Pascal-Mechanismus, wie er in Delphi implementiert ist, als auch den C++ Mechanismus, wie er in Borland C++ zu finden ist. Wenn eine Exception auftritt, wird unabhängig von ihrer Herkunft immer das erwartete Verhalten ausgelöst. Anders ausgedrückt, die Semantik der Exception-Behandlung wurde nicht verändert, sondern erweitert, um die Koexistenz der beiden Mechanismen zu ermöglichen.

Trotzdem sind in C++ und Delphi einige Unterschiede zu beachten:

Exceptions, die von Konstruktoren ausgelöst werden:

In C++ werden Destruktoren für alle Elemente und Basisklassen aufgerufen, die vollständig erzeugt sind.

In Object Pascal werden Destruktoren auch dann für Basisklassen aufgerufen, wenn das Objekt oder die Basisklasse noch nicht vollständig erzeugt sind.

Exceptions abfangen:

C++ Exceptions können per Referenz, Zeiger oder Wert abgefangen werden. Exceptions, die von TObject abgeleitet sind, können nur per Referenz oder Zeiger erfaßt werden. Ein Versuch, TObject-Exceptions per Wert anzusprechen, führt zu einem Compiler-Fehler.

Object Pascal-Exceptions werden per Referenz abgefangen.


Betriebssystem-Exceptions

Siehe auch

C++Builder ermöglicht die Behandlung von Exceptions, die vom Betriebssystem ausgelöst wurden. Betriebssystem-Exceptions umfassen Zugriffsverletzungen, arithmetische Integer-Fehler, Gleitkommafehler, Stapelüberlauf und Programmunterbrechungen mit STRG+C. Diese werden in der C++ RTL behandelt, in Delphi-Exception-Klassenobjekte umgewandelt und an die Anwendung weitergeleitet. In C++ können Sie dann Code folgender Art schreiben:

try

{

   char * p = 0;

   *p = 0;

}

// Exceptions am besten per Referenz abfangen.

catch (const EAccessViolation &e)

{

   printf("Diese Aktion ist nicht möglich!\n");

}

Die Exception-Klassen, die von der RTL verwendet werden, sind notwendigerweise keine ANSI-Klassen, da ANSI keinen solchen Exception-Typ vorsieht. Die Klassen, die wir verwenden, sind exakt dieselben, die auch Delphi verwendet, und sind nur für C++Builder-Anwendungen verfügbar. Sie sind von TObject abgeleitet und benötigen die VCL als Basis.

Folgende Merkmale zeichnen die Exception-Behandlung in C++Builder aus:

Der Programmierer ist nicht verantwortlich dafür, das Exception-Objekt freizugeben.

Betriebssystem-Exceptions können nur per Zeiger oder per Referenz abgefangen werden (Per Referenz ist vorzuziehen).

Der Benutzer kann eine Betriebssystem-Exception kein zweites Mal auslösen, sobald die Ereignisbehandlung verlassen und die Exception von einer Delphi-Ereignisbehandlungsroutine abgefangen wurde.

Der Benutzer kann eine Betriebssystem-Exception kein zweites Mal auslösen, sobald die Ereignisbehandlung verlassen und die Exception von einer Betriebssystem-Ereignisbehandlungsroutine abgefangen wurde.


Delphi-Exceptions

Siehe auch

C++Builder erweitert die Semantik zur Behandlung von Software-Exceptions, die von Delphi oder C++ ausgelöst wurden. In beiden Fällen ist die generierte Exception-Klasse von TObject abgeleitet. Es ergeben sich eine Reihe von Regeln aufgrund der Tatsache, daß VCL-Klassen nur auf dem Heap angelegt werden können.

VCL-Exception-Klassen können nur per Zeiger oder per Referenz abgefangen werden (Referenz ist vorzuziehen).

VCL-Exception-Klassen können nur per Zeiger oder Referenz ausgelöst werden.

Abgesehen von diesen Punkten bestehen keine Einschränkungen. C++Builder bietet keinen Mechanismus, um C++ Exceptions in Delphi abzufangen.


Aspekte der Portierbarkeit

Siehe auch

Mit diesem Produkt werden mehrere Laufzeitbibliotheken (RTLs) geliefert. Die meisten davon beziehen sich auf C++Builder-Anwendungen, aber eine davon (CW32MT.LIB) ist die normale Multithread-RTL, die keine Referenzen zur VCL aufweist. Diese RTL ist für die Unterstützung bestehender Anwendungen gedacht, die Teile eines Projektes sein können, aber nicht auf der VCL basieren. Wegen des fehlenden Bezugs zur VCL unterstützt diese Bibliothek keine Exception-Behandlung, da die entsprechenden Objekte von TObject abgeleitet sind und somit Teile der VCL in der Anwendung benötigen würden.

Wenn Sie die Konzepte von C++Builder in vollem Umfang nutzen wollen, verwenden Sie die Bibliothek CP32MT.LIB. Hier finden Sie Multithread-Operabilität zur Speicherverwaltung und Exception-Behandlung in Verbindung mit der VCL.


Wertebereichsgrenzen
Wertebereichsgenzen

Wenn Sie Open Arrays an Funktionen übergeben, die ein Array von Konstanten akzeptieren (d.h. TVarRec*, int), müssen float- und double-Werte in long double umgewandelt werden (z.B. (long double)3.14159).

Die maximale Anzahl von Open-Array-Elementen, die temporär mit OPENARRAY erzeugt werden können, beträgt 19.


Beispiele

Beispiel 1

Beispiel 2


Beispiele

Beispiel 1

Beispiel 2

Beispiel 3


TDateTime, Datentyp

Siehe auch	Konstruktoren	Elemetfunktionen	Referenz

Beschreibung

TDateTime ist eine C++ Klasse, die den Delphi-Datentyp TDateTime und die Delphi Datums- und Uhrzeit-Laufzeitbibliotheks-Routinen implemetiert, die den Datentyp TDateTime verwenden.

Die Klasse TDateTime erbt ein Datenelemt Val, das als double deklariert ist und den Datums-/Uhrzeitwert enthält

Der Teil vor dem Kamma eines TDateTime-Wertes stellt die Anzahl der Tage dar, die seit dem 30.12.1899 verstrichen sind. Der Teil nach dem Komma eines TDateTime-Wertes ist die Uhrzeit.

Nachfolgend einige Beispiele für TDateTime-Werte und deren entsprechende Datums- und Uhrzeitangaben:

0	   30.12.1899 12:00

2.75	   01.01.1900 18:00

-1.25	   29.12.1899 6:00

35065	   01.01.1996 12:00

Um den Bruchwert der Tage zwischen zwei Datumsangaben zu ermitteln, ziehen Sie die beiden Werte voneinander ab. Um einen Datums- und Uhrzeitwert um eine bestimmten Bruchwert zu erhöhen, fügen Sie den Bruchwert dem Datums- und Uhrzeitwert hinzu.

Hinweis

Verwenden Sie nur die Operatoren innerhalb von TDateTime. Der Compiler wird alle Operatoren, die Sie selbst überladen, ignorieren.


TDateTime, Konstruktoren

Siehe auch

Beschreibung

Verwenden Sie die Konstruktoren, um TDateTime-Objekte aus Zeigern auf andere TDateTime-Objekte, ints, doubles, AnsiStrings, date- oder time-Argumenten zu erzeugen.

__fastcall TDateTime();

Erzeugt ein TDateTime-Objekt mit einen Datums-/Uhrzeitwert von 0.

TDateTime(const TDateTimeBase& src);

TDateTime(const TDateTime& src);

Erzeugt ein TDateTime-Objekt unter Verwendung eines anderen TDateTime-Objekts. TDateTimeBase ist die Basisklasse von TDateTime.

__fastcall TDateTime(const double src);

Erzeugt ein TDateTime-Objekt aus einem double-Wert.

__fastcall TDateTime(const int src);

Erzeugt ein TDateTime-Objekt aus einem int-Wert.

enum TDateTimeFlag {Date, Time, DateTime};

__fastcall TDateTime(const AnsiString& src, TDateTimeFlag flag = DateTime);

Erzeugt ein TDateTime-Objekt aus einem AnsiString. Der Wert des TDateTime-Objekts ist ein Datum, eine Uhrzeit, oder Datum/Uhrzeit, je nachdem, welchen Wert das Argument flag enthält.

__fastcall TDateTime(unsigned short year, unsigned short month,

    unsigned short day);

Erzeugt ein TDateTime-Objekt aus dem Datumswert, der in den Argumenten year, month und day angegeben wurde.

__fastcall TDateTime unsigned short hour, unsigned short min,

    unsigned short sec, unsigned short msec);

Erzeugt ein TDateTime-Objekt aus dem Uhrzeitwert, der in den Argumenten hour, min, sec, und msec angegeben wurde.


TDateTime, Datentyp-Referenz

Siehe auch

Syntax

class __declspec(delphireturn) TDateTime : public TDateTimeBase

Konstruktoren

__fastcall TDateTime()

__fastcall TDateTime(const TDateTimeBase& src)

__fastcall TDateTime(const TDateTime& src)

__fastcall TDateTime(const double src)

__fastcall TDateTime(const int src)

enum TDateTimeFlag {Date, Time, DateTime};

__fastcall TDateTime(const AnsiString& src, TDateTimeFlag flag = DateTime);

__fastcall TDateTime(unsigned short year, unsigned short month,

    unsigned short day);

__fastcall TDateTime(unsigned short hour, unsigned short min,

    unsigned short sec, unsigned short msec);

Operatoren

// Verwenden Sie nur die Operatoren innerhalb von TDateTime.

// Der Compiler wird Operatoren die Sie selbst überladen, ignorieren.

TDateTime& __fastcall operator =(const TDateTimeBase& rhs)

TDateTime& __fastcall operator =(const TDateTime& rhs)

TDateTime& __fastcall operator =(const double rhs)

TDateTime& __fastcall operator =(const int rhs)

TDateTime& __fastcall operator +=(const TDateTimeBase& rhs)

TDateTime& __fastcall operator +=(const TDateTime& rhs)

TDateTime& __fastcall operator +=(const double rhs)

TDateTime& __fastcall operator +=(const int rhs)

TDateTime& __fastcall operator -=(const TDateTimeBase& rhs)

TDateTime& __fastcall operator -=(const TDateTime& rhs)

TDateTime& __fastcall operator -=(const double rhs)

TDateTime& __fastcall operator -=(const int rhs)

TDateTime& operator ++()

TDateTime operator ++(int)

TDateTime& operator --()

TDateTime operator --(int)

TDateTime __fastcall operator +(const TDateTimeBase& rhs) const

TDateTime __fastcall operator +(const TDateTime& rhs) const

TDateTime __fastcall operator +(const double rhs) const

TDateTime __fastcall operator +(const int rhs) const

TDateTime __fastcall operator -(const TDateTimeBase& rhs) const

TDateTime __fastcall operator -(const TDateTime& rhs) const

TDateTime __fastcall operator -(const double rhs) const

TDateTime __fastcall operator -(const int rhs) const

// Vergleiche

bool __fastcall operator ==(const TDateTime& rhs) const

bool __fastcall operator !=(const TDateTime& rhs) const

bool __fastcall operator >(const TDateTime& rhs) const

bool __fastcall operator <(const TDateTime& rhs) const

bool __fastcall operator >=(const TDateTime& rhs) const

bool __fastcall coperator <=(const TDateTime& rhs) const

__fastcall operator AncsiString() const;     //<Date||Time||DateTime>String(smart)

__fastcall operator double() const

__fastcall operator int() const

Public-Elementfunktionen

static TDateTime __fastcall CurrentDate();

static TDateTime __fastcall CurrentTime();

static TDateTime __fastcall CurrentDateTime();

static TDateTime __fastcall FileDateToDateTime(int fileDate);

AnsiString __fastcall FormatString(const AnsiString& format);

AnsiString __fastcall DateString() const;

AnsiString __fastcall TimeString() const;

AnsiString __fastcall DateTimeString() const;

int __fastcall DayOfWeek() const;

int __fastcall FileDate() const;

void __fastcall DecodeDate(unsigned short* year, unsigned short*

  month, unsigned short* day) const;

void __fastcall DecodeTime(unsigned short* hour, unsigned short*

  min, unsigned short* sec, unsigned short* msec) const;


TDateTime, Elementfunktionen

Nachfolgend die Elementfunktionen der Klasse TDateTime.

CurrentDate()

CurrentTime()

CurrentDateTime()

DayOfWeek()

DateString()

DateTimeString()

DecodeDate()

DecodeTime()

FileDate()

FileDateToDateTime()

FormatString()

TimeString()


CurrentDate(), Elementfunktion

Siehe auch

Syntax

static TDateTime __fastcall CurrentDate();

Beschreibung

CurrentDate() liefert das aktuelle Datum in Form eines TDateTime-Wertes.


CurrentTime(), Elementfunktion

Siehe auch

Syntax

static TDateTime __fastcall CurrentTime();

Beschreibung

CurrentTime() liefert die aktuelle Uhrzeit in Form eines TDateTime-Wertes.


CurrentDateTime(), Elementfunktion

Siehe auch

Syntax

static TDateTime __fastcall CurrentDateTime();

Beschreibung

CurrentDateTime() liefert das aktuelle Datum und Uhrzeit in Form eines TDateTime-Wertes.


DayOfWeek(), Elementfunktion

Siehe auch

Syntax

int __fastcall DayOfWeek() const;

Beschreibung

DayOfWeek() liefert den Wochentag des TDateTime-Wertes in Form eines Integer-Wertes zwischen 1 und 7 zurück. Sontag ist der erste Tag der Woche und Samstag der siebte.


DateString(), Elementfunktion

Siehe auch

Syntax

AnsiString __fastcall DateString() const;

Beschreibung

DateString() wandelt das Datum des TDateTime-Wertes in einen String um. Die Umwandlung verwendet das über die Variable ShortDateFormat angegebene Format.


DateTimeString(), Elementfunktion

Siehe auch

Syntax

AnsiString __fastcall DateTimeString() const;

Beschreibung

DateTimeToStr() wandelt den TDateTime-Wert in einen String um. Falls der TDateTime-Wert keinen Datumswert enthält, wird das Datum als 00.00.00 angezeigt. Wenn der TDateTime-Wert keinen Uhrzeitwert enthält, wird die Uhrzeit als 00:00:00 angezeigt. Sie können die Formatierung des Strings ändern, indem Sie die Werte der Datums- und Uhrzeit-Formatierungsvariablen ändern.


DecodeDate(), Elementfunktion

Siehe auch

Syntax

void __fastcall DecodeDate(unsigned short* year, unsigned short* month, unsigned short* day) const;

Beschreibung

DecodeDate() zerlegt den TDateTime-Wert in Jahres-, Monats- und Tages-Werte und speichert diese Werte in den Parametern year, month und day. Verwenden Sie DecodeDate() wenn Sie Zugriff auf Jahres-, Monats- oder Tages-Werte eines TDateTime-Wertes benötigen.


DecodeTime(), Elementfunktion

Siehe auch

Syntax

void __fastcall DecodeTime(unsigned short* hour, unsigned short* min, unsigned short* sec, unsigned short* msec) const;

Beschreibung

DecodeTime() zerlegt den TDateTime-Wert in Stunden-, Minuten-, Sekunden- und Milisekunden-Werte und speichert diese in den Parametern hour, min, sec und msec. Verwenden Sie DecodeDate() wenn Sie Zugriff auf die Stunden-, Minuten-, Sekunden- und Milisekunden-Werte eines TDateTime-Wertes benötigen.


FileDate(), Elementfunktion

Siehe auch

Syntax

int __fastcall FileDate() const;

Beschreibung

FileDate() wandelt Datums- und Uhrzeitwerte in ein DOS Datums- und Uhrzeit-Format um.


FileDateToDateTime(), Elementfunktion

Siehe auch

Syntax

static TDateTime __fastcall FileDateToDateTime(int fileDate);

Bescheibung

FileDateToDateTime() wandelt einen im fileDate-Argument angegebenen DOS-Datei Datums- und Uhrzeitwert in einem TDateTime-Wert um.


FormatString(), Elementfunktion

Siehe auch

Syntax

AnsiString __fastcall FormatString(const AnsiString& format);

Beschreibung

FormatString() liefert den TDateTime-Wert als formatierten String zurück, unter Verwendung des über das Argument format angegebenen Formats. Folgende Format-Spezifizierer werden unterstützt:

Spezifizierer	Anzeige

c	Zeigt das Datum unter Verwendung des in der Variablen ShortDateFormat festgelegten Formats an, gefolgt von der Uhrzeit, unter Verwendung des Formats, das über die Variable LongTimeFormat definiert wird. Die Uhrzeit wird nicht angezeigt, wenn der Teil nach dem Komma des DateTime-Wertes gleich null ist.

d	Zeigt den Tag ohne eine vorangestellte Null an (1-31).

dd	Zeigt den Tag mit einer vorangestellten Null an (01-31).

ddd	Zeigt den Tag in Form einer Abkürzung (So-Sa), unter Verwendung der Srings der Variablen ShortDayNames, an.

dddd	Zeigt den Tag mit seinem Namen (Sonntag-Samstag), unter Verwendung der Srings der Variablen LongDayNames, an.

ddddd	Zeigt das Datum in dem Format an, das durch die Variable ShortDateFormat festgelegt wird.

dddddd	Zeigt das Datum in dem Format an, das durch die Variable LongDateFormat festgelegt wird.

m	Zeigt den Monat als Zahl, ohne vorangestellte Null (1-12), an. Falls der Bezeichner m unmittelbar auf einen Bezeichner h oder hh folgt, wird die Minute anstelle des Monats angezeigt.

mm	Zeigt den Monat als Zahl mit vorangestellter Null an (01-12). Falls der Bezeichner mm unmittelbar auf einen Bezeichner h oder hh folgt, wird die Minute anstelle des Monats angezeigt.

mmm	Zeigt den Monat als Abkürzung (Jan -Dez), unter Verwendung des in der Variabblen ShortMonthNames festgelegten Strings, an.

mmmm	Zeigt den Monat mit seinem vollständigen Namen an (Januar - Dezember) und verwendet hierzu die Strings, aus der Variablen LongMonthNames.

yy	Zeigt das Jahr als zweistellige Zahl an (00-99).

yyyy	Zeigt das Jahr als vierstellige Zahl an (0000-9999).

h	Zeigt die Stunde ohne führende Null an (0-23).

hh	Zeigt die Stunde mit führender Null an (00-23).

n	Zeigt die Minute ohne führende Null an (0-59).

nn	Zeigt die Minute mit führender Null an (00-59).

s	Zeigt die Sekunde ohne führende Null an (0-59).

ss	Zeigt die Sekunde mit einer führenden Null an (00-59).

t	Zeigt die Zeit in dem Format an, das in der Variablen ShortTimeFormat festgelegt ist.

tt	Zeigt die Zeit in dem Format an, das in der Variablen LongTimeFormat angegeben ist.

am/pm	Verwendet das 12-Stunden-Format für den voranstehenden Spezifizierer h oder hh und zeigt 'am' für Zeiten vor 12 Uhr mittags und 'pm' für Zeiten nach 12 Uhr mittags an. Der Spezifizierer am/pm kann in Kleinschreibung, Großschreibung oder in gemischter Schreibweise eingegeben werden und wird entsprechend angezeigt.

a/p	Verwendet das 12-Stunden-Format für den voranstehenden Spezifizierer h oder hh und zeigt 'a' für Zeiten vor 12 Uhr mittags und 'p' für Zeiten nach 12 Uhr mittags an. Der Spezifizierer a/p kann in Kleinschreibung, Großschreibung oder in gemischter Schreibweise eingegeben werden und wird entsprechend angezeigt.

ampm	Verwendet das 12-Stunden-Format für den voranstehenden Spezifizierer h oder hh und zeigt den Inhalt der Variablen TimeAMString für jede Zeit vor 12 Uhr mittags sowie den Inhalt der Variablen TimePMString für jede Uhrzeit nach 12 Uhr mittags an.

/	Zeigt das Datumstrennzeichen an, das in der Variablen DateSeparator angegeben ist.

:	Zeigt das Zeittrennzeichen an, das in der Variablen TimeSeparator angegeben ist.

'xx'/"xx"	Zeichen, die von einfachen oder doppelten Anführungszeichen umgeben sind, werden wie eingegeben angezeigt und beeinflußen die Formatierung nicht.

Formatbezeichner können in Groß- oder in Kleinschreibung eingegeben werden, das Ergebnis wird davon nicht beeinflußt.

Wenn der im Parameter Format angegebene String leer ist, werden der Datums- und der Uhrzeitwert formatiert, als ob ein Formatspezifizierer 'c' gesetzt wäre.


TimeString(), Elementfunktion

Siehe auch

Syntax

AnsiString __fastcall TimeString() const;

Beschreibung

Die Funktion TimeToStr wandelt den Parameter Time, eine Variable vom Typ TDateTime, in einen String um. Die Umwandlung verwendet das durch die globale Variable LongTimeFormat angegebene Format. Sie können das Anzeigeformat des Strings ändern, indem Sie die Werte der Datums- und Uhrzeitvariablen ändern.

TimeToStr() wandelt den TDateTime-Wert in einen String um. Die Umwandlung verwendet das durch die Variable  LongTimeFormat angegebene Format. Sie können das Format für die Anzeige des Strings ändern, indem Sie die Werte der Datums- und Uhrzeit-Formatierungsvariablen ändern.


TDateTime, Formatierungsvariablen

Siehe auch

Syntax

extern char DateSeparator;

extern System::AnsiString ShortDateFormat;

extern System::AnsiString LongDateFormat;

extern char TimeSeparator;

extern System::AnsiString TimeAMString;

extern System::AnsiString TimePMString;

extern System::AnsiString ShortTimeFormat;

extern System::AnsiString LongTimeFormat;

extern System::AnsiString ShortMonthNames[12];

extern System::AnsiString LongMonthNames[12];

extern System::AnsiString ShortDayNames[7];

extern System::AnsiString LongDayNames[7];

Beschreibung

SYSUTILS.HPP enthält eine Anzahl von Variablen, die in der Klasse TDateTime verwendet werden. Sie können diesen Variablen neue Werte zuweisen, um die Formate der Datums- und Uhrzeit-Strings zu ändern. Die Anfangswerte dieser Variablen werden aus der Systemregistrierung, unter Verwendung von GetLocaleInfo() in der Win32-API geholt.

C++Builder-Anwendungen aktualisieren diese Formatierungsvariablen als Antwort auf WM_WININICHANGE-Botschaften automatisch.

Application->UpdateFormatSettings() legt fest, ob Änderungen in den Systemeinstellungen erlaubt sind. Die Vorgabe ist true. Setzen Sie diese Eigenschaft auf false, um zu verhindern, daß die Systemeinstellungen geändert werden.

Die Beschreibung der einzelnen Variablen wird durch die Konstante LOCALE_XXXX angegeben, die zum Holen der Anfangswerte, mit Hilfe von GetLocaleInfo() der Win32-API, verwendet wird.

Variable	Definiert

DateSeparator	DateSeparator ist das Zeichen, das zur Trennung des Jahres-, Monats- und Tagesteils eines Datumswertes verwendet wird. Der Anfangswert wird aus LOCATE_SDATE geholt.

ShortDateFormat	ShortDateFormat ist der Formatierungs-String, der verwendet wird, um einen Datumswert zwecks Editierens in einen kurzen String umzuwandeln. Eine vollständige Beschreibung der Formatierungsstrings für Datum und Uhrzeit finden Sie in der Dokumentation der Funktion FormatString(). Das kurze Datumsformat sollte nur das Trennzeichen und die Formatspezifizierer m, mm, d, dd, yy und yyyy verwenden. Der Anfangswert wird aus LOCALE_SSHORTDATE geholt.

LongDateFormat	LongDateFormat ist der Formatierungsstring, der dazu verwendet wird, einen Datumswert zwecks Anzeige (aber nicht zum Bearbeiten) in einen langen String umzuwandeln. Eine vollständige Beschreibung der Formatierungsstrings für Datum und Uhrzeit finden Sie in der Dokumentation der Funktion FormatString(). Der Anfangswert wird aus LOCALE_SLONGDATE geholt.

TimeSeparator	TimeSeparator ist das Zeichen, das zur Trennung von Stunde, Minute und Sekunde eines Zeitwertes verwendet wird. Der Anfangswert wird aus LOCALE_STIME geholt.

TimeAMString	TimeAMString ist der Suffix-String, der für Zeitwerte zwischen 00:00 und 11:59 im Uhrzeitformat mit 12 Stunden verwendet wird. Der Anfangswert wird aus LOCALE_S1159 geholt.

TimePMString	TimePMString ist der Suffix-String, der für Zeitwerte zwischen 12:00 und 23:59 im Uhrzeitformat mit 12 Stunden verwendet wird. Der Anfangswert wird aus LOCALE_S2359 geholt.

ShortTimeFormat	ShortTimeFormat ist der Formatierungsstring, der verwendet wird, um einen Zeitwert in einen kurzen, nur aus Stunden und Minuten bestehenden String umzuwandeln. Der Anfangswert wird aus LOCALE_ITIME und LOCALE_ITLZERO berechnet.

LongTimeFormat	LongTimeFormat ist der Formatierungsstring, der verwendet wird, um einen Zeitwert in einen langen String mit Stunden, Minuten und Sekunden umzuwandeln. Der Anfangswert wird aus LOCALE_ITIME und LOCALE_ITLZERO berechnet.

ShortMonthNames	ShortMonthNames ist das String-Array, das die abgekürzten Monatsnamen enthält. Der Formatspezifizierer mmm in einem an FormatString() übergebenen Formatierungstring bewirkt die Ersetzung durch einen abgekürzten Monatsnamen. Die Standardwerte werden aus den lokalen Systemeinträgen LOCALE_SABBREVMONTHNAME geholt.

LongMonthNames	LongMonthNames ist das String-Array, das die langen Monatsnamen beinhaltet. Der Formatspezifizierer mmmm in einem an FormatString() übergebenen Formatierungsstring bewirkt die Ersetzung durch einen langen Monatsnamen. Die Standardwerte werden aus den lokalen Systemeinträgen LOCALE_SMONTHNAME geholt.

ShortDayNames	ShortDayNames ist das String-Array, das die abgekürzten Tagesbezeichnungen beinhaltet. Der Formatspezifizierer ddd in einem an FormatString() übergebenen Formatierungsstring bewirkt die Ersetzung durch eine kurze Tagesbezeichnung. Die Standardwerte werden aus den lokalen Systemeinträgen LOCALE_SABBREVDAYNAME geholt.

LongDayNames	LongDayNames ist das String-Array, das die langen Tagesbezeichnungen beinhaltet. Der Formatspezifizierer dddd in einem an FormatString() übergebenen Formatierungsstring bewirkt die Ersetzung durch eine lange Tagesbezeichnung. Die Standardwerte werden aus den lokalen Systemeinträgen LOCALE_SDAYNAME geholt.


Currency, Datentyp

Siehe auch	Konstruktoren	Referenz

Beschreibung

Currency ist eine C++ Klasse, die den Delphi-Datentyp Currency implementiert.

Currency erbt ein Datenelement Val das als int64 deklariert ist und den Währungstypen enthält. Der Bereich der gültigen Währungswerte ist -922337203685477.5808  bis  922337203685477.5807. Verwenden Sie Currency um mit Währungs-, bzw. Geldbeträge zu erfassen.

Hinweis

Verwenden Sie nur die Operatoren mit Currency. Der Compiler wird alle Operatoren, die Sie selbst überladen, ignorieren.


Currency, Datentyp-Referenz

Siehe auch

Syntax

class __declspec(delphireturn) Currency : public CurrencyBase

Konstruktoren

__fastcall Currency()

__fastcall Currency(double val)

__fastcall Currency(int val)

__fastcall Currency(const CurrencyBase& src)

__fastcall Currency(const Currency& src)

__fastcall Currency(const AnsiString& src);

Operatoren

// Verwenden Sie nur die Operatoren mit Currency.

// Der Compiler wird alle Operatoren, die Sie selbst überladen, ignorieren.

friend Currency __fastcall operator +(int lhs, const Currency& rhs);

friend Currency __fastcall operator -(int lhs, const Currency& rhs);

friend Currency __fastcall operator *(int lhs, const Currency& rhs);

friend Currency __fastcall operator /(int lhs, const Currency& rhs);

friend Currency __fastcall operator +(double lhs, const Currency& rhs);

friend Currency __fastcall operator -(double lhs, const Currency& rhs);

friend Currency __fastcall operator *(double lhs, const Currency& rhs);

friend Currency __fastcall operator /(double lhs, const Currency& rhs);

Currency& __fastcall operator =(double rhs)

Currency& __fastcall operator =(int rhs)

Currency& __fastcall operator =(const CurrencyBase& rhs)

Currency& __fastcall operator =(const Currency& rhs)

Currency& __fastcall operator +=(const Currency& rhs)

Currency& __fastcall operator -=(const Currency& rhs)

Currency& __fastcall operator *=(const Currency& rhs)

Currency& __fastcall operator /=(const Currency& rhs)

Currency& __fastcall operator %=(int rhs)

Currency& operator ++()

Currency operator ++(int)

Currency& operator --()

Currency operator --(int)

Currency __fastcall operator +(const Currency& rhs) const

Currency __fastcall operator -(const Currency& rhs) const

Currency __fastcall operator *(const Currency& rhs) const

Currency __fastcall operator /(const Currency& rhs) const

Currency __fastcall operator +(int rhs) const

Currency __fastcall operator -(int rhs) const

Currency __fastcall operator *(int rhs) const

Currency __fastcall operator /(int rhs) const

Currency __fastcall operator %(int rhs) const

Currency __fastcall operator +(double rhs) const

Currency __fastcall operator -(double rhs) const

Currency __fastcall operator *(double rhs) const

Currency __fastcall operator /(double rhs) const

Currency __fastcall operator -() const

Currency __fastcall operator !() const

// Vergleiche (Currency rhs)

bool __fastcall operator ==(const Currency& rhs) const

bool __fastcall operator !=(const Currency& rhs) const

bool __fastcall operator >(const Currency& rhs) const

bool __fastcall operator <(const Currency& rhs) const

bool __fastcall operator >=(const Currency& rhs) const

bool __fastcall operator <=(const Currency& rhs) const

// Vergleiche (int rhs)

bool __fastcall operator ==(int rhs) const

bool __fastcall operator !=(int rhs) const

bool __fastcall operator >(int rhs) const

bool __fastcall operator <(int rhs) const

bool __fastcall operator >=(int rhs) const

bool __fastcall operator <=(int rhs) const

// Vergleiche (double rhs)

bool __fastcall operator ==(double rhs) const

bool __fastcall operator !=(double rhs) const

bool __fastcall operator >(double rhs) const

bool __fastcall operator <(double rhs) const

bool __fastcall operator >=(double rhs) const

bool __fastcall operator <=(double rhs) const

__fastcall operator double() const

__fastcall operator int() const

__fastcall operator AnsiString() const;

_______________________________________________________________________

inline Currency __fastcall operator +(int lhs, const Currency& rhs)

inline Currency __fastcall operator -(int lhs, const Currency& rhs)

inline Currency __fastcall operator *(int lhs, const Currency& rhs)

inline Currency __fastcall operator /(int lhs, const Currency& rhs)

inline Currency __fastcall operator +(double lhs, const Currency& rhs)

inline Currency __fastcall operator -(double lhs, const Currency& rhs)

inline Currency __fastcall operator *(double lhs, const Currency& rhs)

inline Currency __fastcall operator /(double lhs, const Currency& rhs)

inline ostream& operator <<(ostream& os, const Currency& arg)

inline istream& operator >>(istream& is, Currency& arg)


Currency, Konstruktoren

Siehe auch

Beschreibung

Verwenden Sie die Currency-Konstruktoren, um Currency-Objekte aus Zeigern auf andere Currency-Objekte, ints, doubles, AnsiStrings zu erzeugen.

__fastcall Currency();

Erzeugt ein Currency-Objekt mit einem Währungsbetrag 0.

__fastcall Currency(double val);

Erzeugt ein Currency-Objekt aus einem double-Wert.

__fastcall Currency(int val);

Erzeugt ein Currency-Objekt aus einem int-Wert.

__fastcall Currency(const CurrencyBase& src);

__fastcall Currency(const Currency& src);

Erzeugt ein Currency-Objekt unter Verwendung eines anderen Currency-Objekts. CurrencyBase ist die Basisklasse von Currency; sie beinhaltet nur das Datenelement Val, welches den Währungsbetrag enthält.

__fastcall Currency(const AnsiString& src);

Erzeugt ein Currency-Objekt aus einem AnsiString.


Currency, Formatierungsvariablen

Siehe auch

Syntax

extern System::AnsiString CurrencyString;

extern unsigned char CurrencyFormat;

extern unsigned char NegCurrFormat;

extern char ThousandSeparator;

extern char DecimalSeparator;

extern unsigned char CurrencyDecimals;

Beschreibung

SYSUTILS.HPP enthält eine Anzahl von Variablen, die in der Klasse Currency verwendet werden. Sie können diesen Variablen neue Werte zuweisen, um die Formatierung der Currency-Werte zu ändern. Die Anfangswerte dieser Variablen werden aus der Systemregistrierung, unter Verwendung von GetLocaleInfo() in der Win32-API geholt.

C++Builder-Anwendungen aktualisieren diese Formatierungsvariablen als Antwort auf WM_WININICHANGE-Botschaften automatisch.

Application->UpdateFormatSettings() legt fest, ob Änderungen in den Systemeinstellungen erlaubt sind. Die Vorgabe ist true. Setzen Sie diese Eigenschaft auf false, um zu verhindern, daß die Systemeinstellungen geändert werden.

Die Beschreibung der einzelnen Variablen wird durch die Konstante LOCALE_XXXX angegeben, die zum Holen der Anfangswerte, mit Hilfe von GetLocaleInfo() der Win32-API, verwendet wird.

Variable	Definiert

CurrencyString	CurrencyyString definiert das Währungssymbol (oder Zeichen), das bei Umwandlung von Gleitkomma- in Dezimalzahlen verwendet wird. Der Anfangswert wird aus LOCALE_SCURRENCY geholt.

CurrencyFormat	CurrencyFormat definiert die Plazierung und den Separator des Währungssymbols, die bei Umwandlung von Gleitkomma- in Dezimalzahlen verwendet werden. Mögliche Werte dafür sind:

0 = 'DM1'

1 = '1DM'

2 = 'DM 1'

3 = '1 DM'

Der Anfangswert wird aus LOCALE_ICURRENCY geholt.

NegCurrFormat	NegCurrFormat definiert das Währungsformat, das bei Umwandlung von negativen Gleitkomma- in Dezimalzahlen verwendet wird. Mögliche Werte dafür sind:

0 = (DM1)	8 = -1 DM

1 = -DM1	9 = -DM 1

2 = DM-1	10 = 1 DM-

3 = DM1-	11 = DM 1-

4 = (1DM)	12 = DM -1

5 = -1DM	13 = 1- DM

6 = 1-DM	14 = (DM 1)

7 = 1DM-	15 = (1 DM)

Der Anfangswert wird aus LOCALE_INEGCURR Geholt.

ThousandSeparator	ThousandSeparator ist das Zeichen, das als Trennzeichen für die Tausender bei Zahlen mit mehr als drei Ziffern links des Dezimaltrennzeichens verwendet wird. Der Anfangswert wird aus LOCALE_STHOUSAND geholt.

DecimalSeparator	DecimalSeparator ist das Zeichen, das in einer Zahl den ganzzahligen Teil vom Bruchteil trennt. Der Anfangswert wird aus LOCALE_SDECIMAL geholt.

CurrencyDecimals	CurrencyDecimals ist die Anzahl von Ziffern rechts des Dezimalkommas im Währungsbetrag. Der Anfangswert wird aus LOCALE_ICURRDIGITS geholt.


Erweiterte Delphi-Datentypen

Siehe auch

Einige komplexe Datentypen von Object Pascal und Delphi können nicht einfach mit typedef für die C++Builder-Konventionen umgewandelt werden. C++Builder bietet eine erweiterte Unterstützung für solche Datentypen, indem diese als C++ Klassen implementiert sind. Die erweiterten Delphi-Datentypen sind in der Header-Datei INCLUDE\VCL\SYSDEFS.H definiert. Jede Klasse enthält alle notwendigen Konstruktoren, Elementfunktionen und Operatoren.

Verwenden Sie die entsprechende C++Builder-Implementierung dieser Typen, wenn Sie mit der VCL arbeiten oder eigene Komponenten entwickeln.

Folgende Delphi-Datentypen sind als C++ Klassen definiert:

Set

AnsiString

Variant

TDateTime

Currency


Datentyp Set

Siehe auch	Beispiel 	Referenz

Der Typ Set dient, so wie er in INCLUDE\VCL\SYSDEFS.H definiert ist, zur Definition von Typen, die als VCL-Funktionsparameter oder -Rückgabewerte verwendet werden.

Die Vorlagenklasse Set<type, minval, maxval> ist eine C++ Klasse und implementiert den intrinsischen Delphi-Typ set. Drei Parameter sind erforderlich:

type: Der Typ der Mengenelemente (normalerweise int, char oder enum).

minval: Das Minimum der Menge (kann nicht kleiner als 0 sein)

maxval: Das Maximum der Menge (kann nicht größer als 255 sein)

Jede Instanz von Set erzeugt ein Objekt, das auf allen drei Parametern basiert. Deshalb sind die beiden folgenden Typen unterschiedlich:

Set <char, ‘A’, ‘C’> s1;

Set <char, ‘X’, ‘Z’> s2;

if (s1 == s2)   // Fehler, unzulässige struct

Um mehrere Instanzen von Set zu erzeugen, verwenden Sie einen typedef-Ausdruck.

typedef Set <char, ‘A’,‘Z’> UPPERCASESet;

Mit der Deklaration einer Variable des Typs Set wird diese nicht initialisiert. Deklaration und Initialisierung erfolgen so:

UPPERCASESet s1;

s1 << ‘A’ << ‘B’ << ‘C’;   // Initialisieren

UPPERCASESet s2;

s2 << ‘X’ << ‘Y’ << ‘Z’;   // Initialisieren


Set

Siehe auch

Syntax

template<class T, unsigned char minEl, unsigned char maxEl>

class __declspec(delphireturn) Set;

Public-Konstruktor

__fastcall Set();

__fastcall Set(const Set& src);

Public-Elementfunktionen

bool __fastcall Contains(const T el) const;

Set& __fastcall Clear();

Public-Operatoren

Set& __fastcall operator =(const Set& rhs);

Set& __fastcall operator +=(const Set& rhs); //Vereinigungsmenge

Set& __fastcall operator -=(const Set& rhs); //Differenzmenge

Set& __fastcall operator *=(const Set& rhs); //Schnittmenge

Set __fastcall operator +(const Set& rhs) const; //Vereinigungsmenge

Set __fastcall operator -(const Set& rhs) const; //Differenzmenge

Set __fastcall operator *(const Set& rhs) const; //Schnittmenge

Set& __fastcall operator <<(const T el); //Element hinzufügen

Set& __fastcall operator >>(const T el); //Element entfernen

bool __fastcall operator ==(const Set& rhs) const;

bool __fastcall operator !=(const Set& rhs) const ;


Beispiel

#include <sysdefs.h>

#include <iostream.h>

typedef Set<char,'U','Z'> UpperSet;

typedef Set<char,'a','z'> LowerSet;

typedef Set<char,'a','j'> HalfLowerSet;

void set_example()

{

LowerSet ae, ae2, ac, de;

UpperSet AE, AE2, AC, DE;

HalfLowerSet aj;

// Beide Anweisungen ergeben false. Mengen sind leer, bis Elemente eingefügt

// werden.

cout <<"Set ae " << (ae.Contains('a')?"does":"does not") << " contain 'a' " << endl;

cout <<"Set AE " << (AE.Contains('C')?"does":"does not") << " contain 'C' " << endl;

ae  << 'a' << 'b' << 'c' << 'd' << 'e';

ae2 << 'a' << 'b' << 'c' << 'd' << 'e';

ac  << 'a' << 'b' << 'c';

de  << 'd' << 'e';

aj  << 'd' << 'e';

DE  << 'D' << 'E';

cout <<"Now, set ae " << (ae.Contains('a')?"does":"does not") << " contain 'a' " << endl;

/***

 Compiler meldet Fehler! Zwar sind in den Mengen 'de' und 'aj' dieselben Elemente enthalten, aber der Typ ist unterschiedlich. Ein Vergleich ist nicht möglich.

****/

//cout << (de==aj?" == ":" != ") << endl;

// Mengen erlauben Operationen wie * (Schnittmenge),+ (Vereinigungsmenge)

// und - (Differenzmenge).

cout << "Set ae " << (ae==(ac+de)?" == ":" != ") << "set ac + de." << endl;

cout << "Set de " << (de==(ae-ac)?" == ":" != ") << "set ae - ac." << endl;

// Element löschen

cout << "Set ae2: " << ae2 << endl;

ae2.Clear();

cout << "Set ae2: " << ae2 << " after ae2.Clear()" << endl;

// Mengen erlauben Operationen wie * (Schnittmenge),+ (Vereinigungsmenge)

// und - (Differenzmenge).

ae2+=ac;

cout << "Union ae2+=ac: Set ae2 " << (ae2==ac?" == ":" != ") << "set ac." << endl;

ae2+=de;

cout << "Union ae2+=de: Set ae2 " << (ae2==ae?" == ":" != ") << "set ae." << endl;

ae2-=ac;

cout << "Difference ae2-=ac: Set ae2 " << (ae2==de?" == ":" != ") << "set de." << endl;

}

int main()

{

   set_example();

return 0;

}


Datentyp AnsiString

Siehe auch	Beispiel	Referenz

C++Builder implementiert den Typ AnsiString als Klasse. AnsiString funktioniert wie die langen String-Typen in Delphi. Entsprechend umfaßt auch AnsiString die folgenden String-Merkmale, die für VCL-Funktionen erforderlich sind, welche die langen String-Typen von Delphi verwenden:

Referenzzähler

String-Länge

Daten

Endezeichen für leere Strings

Wenn Sie keine Initialisierung vornehmen, sind AnsiString-Variablen mit NULL initialisiert.


Beispiel

/* Compilieren mit bcc32 famille.cpp vcl.lib ole2w32.lib */

#include <vcl/dstring.h>

#include <stdio.h>

class Famille

{

private:

   AnsiString FNames[10];

   AnsiString GetName(int Index);

   void SetName(int, AnsiString);

public:

    __property AnsiString Names[int Index] = {read=GetName, write=SetName};

   Famille(){}

   ~Famille(){}

};

AnsiString Famille::GetName(int i)

{

   return FNames[i];

}

void Famille::SetName(int i,const AnsiString s)

{

   FNames[i]=s;

}

int main()

{

   Famille C;

   C.Names[0]="Steve";   //Aufruf von Famille::SetName()

   C.Names[1]="Amy";

   C.Names[2]="Sarah";

   C.Names[3]="Andrew";

   for (int i = 0; i <= 3; i++)

   {

      //Aufruf von Famille::GetName()

      puts(C.Names[i].c_str());

   }

}


AnsiString
Datentyp AnsiString – Referenz

Siehe auch

Syntax

class __declspec(delphireturn) AnsiString

Public-Konstruktoren

__fastcall AnsiString();

Erzeugt einen leeren String.

__fastcall AnsiString(const char* src);

__fastcall AnsiString(const AnsiString& src);

__fastcall AnsiString(const char* src, unsigned char len);

__fastcall AnsiString(const wchar_t* src);

__fastcall AnsiString(int src);

__fastcall AnsiString(double src);

Public-Desruktor

__fastcall ~AnsiString();

Public-Datenelement

enum TStringFloatFormat {sffGeneral, sffExponent, sffFixed, sffNumber, sffCurrency };

Die Aufzählung TStringFloatFormat wird von FloatToStrF verwendet.

friend AnsiString __fastcall operator +(const char*, const AnsiString& rhs);

static AnsiString __fastcall StringOfChar(char ch, int count);

static AnsiString __fastcall LoadStr(int ident);

static AnsiString __fastcall FmtLoadStr(int ident, const TVarRec *args, int size);

static AnsiString __fastcall Format(const AnsiString& format,const TVarRec *args, int size);

static AnsiString __fastcall FormatFloat(const AnsiString& format,const long double& value);

static AnsiString __fastcall FloatToStrF(long double value, TStringFloatFormat format, int precision, int digits);

static AnsiString __fastcall IntToHex(int value, int digits);

static AnsiString __fastcall CurrToStr(Currency value);

static AnsiString __fastcall CurrToStrF(Currency value, TStringFloatFormat format, int digits);

Zuweisungen

AnsiString& __fastcall operator =(const AnsiString& rhs);

AnsiString& __fastcall operator +=(const AnsiString& rhs);

operator +=() wird nicht unterstützt, wenn die Eigenschaft über Abruf- oder Zuweisungsfunktionen verfügt.

Vergleiche

bool __fastcall operator ==(const AnsiString& rhs) const;

bool __fastcall operator !=(const AnsiString& rhs) const;

bool __fastcall operator <(const AnsiString& rhs) const;

bool __fastcall operator >(const AnsiString& rhs) const;

bool __fastcall operator <=(const AnsiString& rhs) const;

bool __fastcall operator >=(const AnsiString& rhs) const;

int __fastcall AnsiCompare(const AnsiString& rhs) const;

int __fastcall AnsiCompareIC(const AnsiString& rhs) const; //Schreibweise

                                                           //ignorieren

char& __fastcall operator [](const int idx);

Der Operator [] setzt die Indexbasis 1 voraus.

Verkettung

AnsiString __fastcall operator +(const AnsiString& rhs) const;

C-String-Operator

char* __fastcall c_str() const

int __fastcall Length() const;

bool __fastcall IsEmpty() const;

void __fastcall Unique();

Mit dieser Funktion wird ein String eindeutig gemacht (refcnt == 1).

void __fastcall Insert(const AnsiString& str, int index);

void __fastcall Delete(int index, int count);

void __fastcall SetLength(int newLength);

int __fastcall Pos(const AnsiString& subStr) const;

AnsiString __fastcall LowerCase() const;

AnsiString __fastcall UpperCase() const;

AnsiString __fastcall Trim() const;

Die Funktion Trim kann Leerzeichen vor und nach dem ersten angezeigten Zeichen entfernen.

AnsiString __fastcall TrimLeft() const;

Die Funktion kann Leerzeichen vor dem ersten angezeigten Zeichen entfernen.

AnsiString __fastcall TrimRight() const;

Die Funktion kann Leerzeichen nach dem ersten angezeigten Zeichen entfernen.

AnsiString __fastcall SubString(int index, int count) const;

int __fastcall ToInt() const;

int __fastcall ToIntDef(int defaultValue) const;

double __fastcall ToDouble() const;

Konvertierung in Unicode

int __fastcall WideCharBufSize() const;

wchar_t* __fastcall WideChar(wchar_t* dest, int destSize) const;

MultiByte-Unterstützung

Diese Funktionen werden mit _MBCS verfügbar.

enum TStringMbcsByteType { mbSingleByte, mbLeadByte, mbTrailByte };

TStringMbcsByteType __fastcall ByteType(int index) const;

bool __fastcall IsLeadByte(int index) const;

bool __fastcall IsTrailByte(int index) const;

bool __fastcall IsDelimiter(const AnsiString& delimiters, int index) const;

bool __fastcall IsPathDelimiter(int index) const;

int __fastcall LastDelimiter(const AnsiString& delimiters) const;

int __fastcall AnsiPos(const AnsiString& subStr) const;

char* __fastcall AnsiLastChar() const;


AnsiString-Multibyte-Funktionen

Siehe auch

Syntax

enum TStringMbcsByteType { mbSingleByte, mbLeadByte, mbTrailByte };

Die Aufzählung TStringMbcsByteType definiert die MBCS-Bytes, die in einem String enthalten sein können.

TStringMbcsByteType __fastcall ByteType(int index) const;

Die Funktion ByteType gibt einen Wert zurück, der angibt, welche Art von Byte an einer bestimmten Indexposition existiert. Bei Treibern für westliche Sprachen wird immer mbSingleByte zurückgegeben. Bei Multibyte-Treibern für fernöstliche Sprachen kann auch mbLeadByte zurückgegeben werden, was anzeigt, daß das Byte das erste in einer Multibyte-Zeichenfolge ist. Der Rückgabewert mbTrailByte zeigt an, daß das Byte das zweite in einer Multibyte-Zeichenfolge ist.

Rückgabewert	Byte mit angegebenem Index
mbSingleByte	wenn kein MBCS, erstes oder zweites Byte
mbLeadByte	wenn MBCS, erstes Byte
mbTrailByte	wenn MBCS, zweites Byte
bool __fastcall IsLeadByte(int index) const;

Die Funktion IsLeadByte gibt einen booleschen Wert zurück, der anzeigt, ob es sich bei dem Byte an der Indexposition um das erste Byte einer Multibyte Zeichenfolge handelt.

bool __fastcall IsTrailByte(int index) const;

Die Funktion IsTrailByte gibt einen booleschen Wert zurück, der anzeigt, ob es sich bei dem Byte an der Indexposition um das zweite Byte einer Multibyte-Zeichenfolge handelt.

bool __fastcall IsDelimiter(const AnsiString& delimiters, int index) const;

Die Funktion IsDelimiter gibt den Wert true zurück, wenn das Zeichen an der Position index mit einem der Zeichen im String delimiters übereinstimmt und wenn das Zeichen nicht das erste oder nachfolgende Byte in einer Multibyte-Sequenz ist. Der String kann Multibyte-Zeichen enthalten, Begrenzer dürfen nur aus Einzel-Byte-Zeichen bestehen.

bool __fastcall IsPathDelimiter(int index) const;

Die Funktion IsPathDelimiter gibt true zurück, wenn das Zeichen an der Position index ein Pfadbegrenzer ist (‘\’), aber nicht das erste oder nachfolgende Byte in einer Multibyte-Sequenz.

int __fastcall LastDelimiter(const AnsiString& delimiters) const;

Die Funktion LastDelimiter gibt den String-Index des am weitesten rechts stehenden ganzen Zeichens zurück, das mit einem Zeichen des Begrenzer-Strings übereinstimmt. Der String kann Multibyte-Zeichen enthalten, Begrenzer dürfen nur aus Einzel-Byte-Zeichen bestehen.

int __fastcall AnsiPos(const AnsiString& subStr) const;

Die Funktion AnsiPos ähnelt der Delphi-Funktion Pos(), mit dem Unterschied, daß hier Multibyte-Strings unterstützt werden.

char* __fastcall AnsiLastChar() const;

Die Funktion AnsiLastChar gibt einen Zeiger auf das letzte Zeichen in einem Multibyte-String zurück. Wenn das letzte Zeichen ein Multibyte-Zeichen ist, verweist der Zeiger auf das Führungs-Byte (das erste Byte des Zeichens).

//Konvertierung in Unicode

int __fastcall WideCharBufSize() const;

wchar_t* __fastcall WideChar(wchar_t* dest, int destSize) const;

Beschreibung

Diese AnsiString-Elementfunktionen dienen der Unterstützung von internationalen Anwendungen.


Datentyp Variant

Siehe auch	Elementfunktionen	Referenz	Beispiel

Deklaration

class __declspec(delphireturn) Variant: public TVarData

Beschreibung

Die Klasse Variant ist die C++Builder-Implementierung des intrinsischen Delphi-Typs Variant.

In C++Builder unterscheidet sich die Syntax für den Typ Variant von den Konventionen in Delphi. Betrachten wir als Beispiel den folgenden Delphi-Code:

V: Variant;

V := VarArrayCreate([0,HighVal,0,HighVal],varInteger);

In C++Builder können Variant und OPENARRAY folgendermaßen verwendet werden:

Variant V(OPENARRAY(int,(0,HighVal,0,HighVal)),varInteger);

Der Typ Variant kann Werte darstellen, die ihren Typ dynamisch ändern. Während andere Variablen statisch an einen Typ gebunden sind, kann eine Variant-Variable zur Laufzeit die unterschiedlichsten Typen annehmen. Der Typ Variant wird meist eingesetzt, wenn der wirkliche Typ, der verarbeitet werden muß, während der Compilierung nicht bekannt ist.

Eine Variante zeichnet sich durch diese Merkmale aus:

•	Varianten können folgendes enthalten: Integer, Realzahlen, Strings, boolesche Werte, Datum und Uhrzeit sowie OLE-Automatisierungsobjekte. Außerdem können Varianten Arrays variabler Größe und Dimensionszahl enthalten, in denen wiederum Elemente der beschriebenen Typen enthalten sein können.

•	Der spezielle Wert Unassigned signalisiert, daß einer Varianten noch kein Wert zugewiesen wurde. Der Wert Null kennzeichnet unbekannte oder fehlende Daten.

•	Eine Variante kann mit anderen kombiniert und aus den folgenden Datentypen aufgebaut werden. Der Compiler übernimmt automatisch die erforderlichen Typkonvertierungen.

short

int

float

double

Currency

TDateTime

bool

WordBool

Byte

AnsiString&

char *

wchar_t * const

Ole2::IDispatch* const

Ole2::IUnknown* const

Wenn eine Variante ein OLE-Automatisierungsobjekt enthält, können mit der Variante die Eigenschaftswerte des Objekts gelesen und gesetzt sowie dessen Methoden aufgerufen werden.

Bei ihrer Erzeugung werden Varianten mit dem Wert Unassigned initialisiert. Dies gilt unabhängig davon, ob eine Variante global, lokal oder Teil einer Struktur, wie etwa eines Arrays, ist.

Varianten bieten zwar außerordentliche Flexibilität, verbrauchen aber auch mehr Speicherplatz als normale Variablen. Ebenso wird für die Verarbeitung von Varianten erheblich mehr Zeit benötigt als bei Werten mit statischem Typ.


Variant (Referenz)

Beispiel

Syntax

class __declspec(delphireturn) Variant: public TVarData

private:

Variant& __fastcall operator [];

friend class AutoCmd;

friend ostream& operator <<(ostream& os, const Variant& arg);

Konstruktoren

__fastcall Variant();

__fastcall Variant(const Variant& src);

//Konstruktoren per Wert

__fastcall Variant(const short src);

__fastcall Variant(const int src);

__fastcall Variant(const float src);

__fastcall Variant(const double src);

__fastcall Variant(const Currency src);

__fastcall Variant(const TDateTime src);

__fastcall Variant(const bool src);

__fastcall Variant(const WordBool src);

__fastcall Variant(const Byte src);

__fastcall Variant(const AnsiString& src);

__fastcall Variant(const char* src);

__fastcall Variant(wchar_t* const src);

__fastcall Variant(Ole2::IDispatch* const src);

__fastcall Variant(Ole2::IUnknown* const src);

Konstruktoren per Referenz

__fastcall Variant(short* src);

__fastcall Variant(int* src);

__fastcall Variant(float* src);

__fastcall Variant(double* src);

__fastcall Variant(Currency* src);

__fastcall Variant(TDateTime* src);

__fastcall Variant(WordBool* src);

__fastcall Variant(Byte* src);

__fastcall Variant(wchar_t** src);

// Konstruktor für Array von Varianten des Typs varType

__fastcall Variant(const int* bounds, const int boundsSize, Word varType);

// Konstruktor für eindimensionales Array des Typs Variant

__fastcall Variant(const Variant* values, const int valuesSize);

Destruktor

__fastcall ~Variant();

Operatoren

Verwenden Sie nur die Operatoren, die in Variant deklariert sind.

Der Compiler ignoriert alle Operatoren, die Sie selbst überladen.

Zuweisungen

Variant& __fastcall operator =(const Variant& rhs);

Variant& __fastcall operator +=(const Variant& rhs);

Variant& __fastcall operator -=(const Variant& rhs);

Variant& __fastcall operator *=(const Variant& rhs);

Variant& __fastcall operator /=(const Variant& rhs);

Variant& __fastcall operator %=(const Variant& rhs);

Variant& __fastcall operator &=(const Variant& rhs);

Variant& __fastcall operator |=(const Variant& rhs);

Variant& __fastcall operator ^=(const Variant& rhs);

Variant& __fastcall operator <<=(const Variant& rhs);

Variant& __fastcall operator >>=(const Variant& rhs);

Vergleiche (Variante steht rechts)

bool __fastcall operator ==(const Variant& rhs) const;

bool __fastcall operator !=(const Variant& rhs) const;

bool __fastcall operator <(const Variant& rhs) const;

bool __fastcall operator >(const Variant& rhs) const;

bool __fastcall operator <=(const Variant& rhs) const;

bool __fastcall operator >=(const Variant& rhs) const;

Vergleiche (int steht rechts)

bool __fastcall operator ==(int rhs) const

bool __fastcall operator !=(int rhs) const

bool __fastcall operator < (int rhs) const

bool __fastcall operator > (int rhs) const

bool __fastcall operator <=(int rhs) const

bool __fastcall operator >=(int rhs) const

Vergleiche (double steht rechts)

bool __fastcall operator ==(double rhs) const

bool __fastcall operator !=(double rhs) const

bool __fastcall operator < (double rhs) const

bool __fastcall operator > (double rhs) const

bool __fastcall operator <=(double rhs) const

bool __fastcall operator >=(double rhs) const

Binäre Operatoren (Variante steht rechts)

Variant __fastcall operator +(const Variant& rhs) const;

Variant __fastcall operator -(const Variant& rhs) const;

Variant __fastcall operator *(const Variant& rhs) const;

Variant __fastcall operator /(const Variant& rhs) const;

Variant __fastcall operator %(const Variant& rhs) const;

Variant __fastcall operator &(const Variant& rhs) const;

Variant __fastcall operator |(const Variant& rhs) const;

Variant __fastcall operator ^(const Variant& rhs) const;

Variant __fastcall operator <<(const Variant& rhs) const;

Variant __fastcall operator >>(const Variant& rhs) const;

Binäre Operatoren (AnsiString steht rechts)

Variant __fastcall operator -(const AnsiString& rhs) const

Variant __fastcall operator *(const AnsiString& rhs) const

Variant __fastcall operator /(const AnsiString& rhs) const

Variant __fastcall operator %(const AnsiString& rhs) const

Variant __fastcall operator &(const AnsiString& rhs) const

Variant __fastcall operator |(const AnsiString& rhs) const

Variant __fastcall operator ^(const AnsiString& rhs) const

Variant __fastcall operator <<(const AnsiString& rhs) const

Variant __fastcall operator >>(const AnsiString& rhs) const

Binäre Operatoren (int steht rechts)

Variant __fastcall operator +(int rhs) const

Variant __fastcall operator -(int rhs) const

Variant __fastcall operator *(int rhs) const

Variant __fastcall operator /(int rhs) const

Variant __fastcall operator %(int rhs) const

Variant __fastcall operator &(int rhs) const

Variant __fastcall operator |(int rhs) const

Variant __fastcall operator ^(int rhs) const

Variant __fastcall operator <<(int rhs) const

Variant __fastcall operator >>(int rhs) const

Binäre Operatoren (double steht rechts)

Variant __fastcall operator +(double rhs) const

Variant __fastcall operator -(double rhs) const

Variant __fastcall operator *(double rhs) const

Variant __fastcall operator /(double rhs) const

Variant __fastcall operator %(double rhs) const

Variant __fastcall operator &(double rhs) const

Variant __fastcall operator |(double rhs) const

Variant __fastcall operator ^(double rhs) const

Variant __fastcall operator <<(double rhs) const

Variant __fastcall operator >>(double rhs) const

Unäre Operatoren

Variant __fastcall operator -() const;

Variant __fastcall operator !() const;

Umwandlungsoperatoren

__fastcall operator short() const;

__fastcall operator int() const;

__fastcall operator float() const;

__fastcall operator double() const;

__fastcall operator Currency() const;

__fastcall operator TDateTime() const;

__fastcall operator bool() const;

__fastcall operator WordBool() const;

__fastcall operator Byte() const;

__fastcall operator AnsiString() const;

__fastcall operator Ole2::IDispatch*();

__fastcall operator Ole2::IUnknown*();

Operatoren für Umwandlung per Referenz

__fastcall operator short*();

__fastcall operator int*();

__fastcall operator float*();

__fastcall operator double*();

__fastcall operator Currency*();

__fastcall operator TDateTime*();

__fastcall operator WordBool*();

__fastcall operator Byte*();

__fastcall operator wchar_t**();

Public-Elementfunktionen

void __fastcall SetError(const Integer err);

Integer __fastcall GetError() const;

void __fastcall Clear();

Variant& __fastcall ChangeType(int VarType);

Variant __fastcall AsType(int VarType) const;

int __fastcall Type() const;

bool __fastcall IsNull() const;

bool __fastcall IsEmpty() const;

public:

static Variant __fastcall CreateObject(const String& ProgID);

static Variant __fastcall GetActiveObject(const String& ProgID);

Array-Bearbeitung

Verwenden Sie die Funktionen GetElement und PutElement für den Zugriff auf variante Arrays.

Der Array-Subskript-Operator ist als private deklariert und kann nur in Verbindung mit varianten Arrays eingesetzt werden.

bool    __fastcall IsArray() const;

Variant __fastcall GetElement(const int i1) const;

Variant __fastcall GetElement(const int i1, const int i2) const;

Variant __fastcall GetElement(const int i1, const int i2, const int i3)

const;

Variant __fastcall GetElement(const int i1, const int i2, const int i3,

const int i4) const;

Variant __fastcall GetElement(const int i1, const int i2, const int i3,

const int i4, const int i5) const;

void __fastcall PutElement(const Variant& data, const int i1);

void __fastcall PutElement(const Variant& data, const int i1, const int i2);

void __fastcall PutElement(const Variant& data, const int i1, const int i2,

const int i3);

void __fastcall PutElement(const Variant& data, const int i1, const int i2,

const int i3, const int i4);

void __fastcall PutElement(const Variant& data, const int i1, const int i2,

const int i3, const int i4, const int i5);

int __fastcall ArrayDimCount() const;

int __fastcall ArrayLowBound(const int dim = 1) const;

int __fastcall ArrayHighBound(const int dim = 1) const;

void __fastcall ArrayRedim(int highBound);

Pointer __fastcall ArrayLock();

void    __fastcall ArrayUnlock();

OLE-Automatisierung

Variant __fastcall Exec(AutoCmd& cmd, Integer lcid = LOCALE_SYSTEM_DEFAULT);

Die nächsten vier Funktionen bieten eine alternative Syntax, ähnlich zu Exec.

void OleProcedure(const String& name, Variant& v0 = Variant(),

Variant& v1 = Variant(),Variant& v2 = Variant(),Variant& v3 = Variant(),

Variant& v4 = Variant(),Variant& v5 = Variant(),Variant& v6 = Variant(),

Variant& v7 = Variant(),Variant& v8 = Variant(),Variant& v9 = Variant());

Variant OleFunction(const String& name, Variant& v0 = Variant(),

Variant& v1 = Variant(),Variant& v2 = Variant(),Variant& v3 = Variant(),

Variant& v4 = Variant(),Variant& v5 = Variant(),Variant& v6 = Variant(),

Variant& v7 = Variant(),Variant& v8 = Variant(),Variant& v9 = Variant());

Variant OlePropertyGet(const String& name, Variant& v0 = Variant(),

Variant& v1 = Variant(),Variant& v2 = Variant(),Variant& v3 = Variant(),

Variant& v4 = Variant(),Variant& v5 = Variant(),Variant& v6 = Variant(),

Variant& v7 = Variant(),Variant& v8 = Variant(),Variant& v9 = Variant());

void OlePropertySet(const String& name, Variant& v0 = Variant(),

Variant& v1 = Variant(),Variant& v2 = Variant(),Variant& v3 = Variant(),

Variant& v4 = Variant(),Variant& v5 = Variant(),Variant& v6 = Variant(),

Variant& v7 = Variant(),Variant& v8 = Variant(),Variant& v9 = Variant());

// Ende der alternativen Syntax für Automatisierung


Elementfunktionen von Variant

Siehe auch

Die Klasse Variant besitzt folgende als public deklarierte Elementfunktionen:

AsType()

ChangeType()

Clear()

CreateObject()

Exec()

GetActiveObject()

OleFunction()

OleProcedure()

OlePropertyGet()

OlePropertySet()

Type()


Konstruktoren von Variant

Siehe auch

Konstruktoren per Wert

Konstruktoren per Referenz

__fastcall Variant();

Standard-Konstruktor

__fastcall Variant(const Variant& src);

Kopier-Konstruktor

__fastcall Variant(const int* bounds, const int boundsSize, Word varType);

Konstruktor für Arrays von Varianten des Typs varType

__fastcall Variant(const Variant* values, const int valuesSize);

Konstruktor für eindimensionale Arrays des Typs Variant

__fastcall ~Variant();

Public-Desruktor


Konstruktoren von Variant - Erzeugung per Wert
Konstruktoren von Variant – Erzeugung per Wert

Siehe auch

Dies sind die Datentypen, mit denen ein Datentyp der Klasse Variant erzeugt werden kann. Abhängig vom übergebenen Argument verwendet der Compiler den Konstruktor für die Erzeugung per Wert oder per Referenz.

__fastcall Variant(const short src);

Beispiel: Variant vSmallint(short(3));

__fastcall Variant(const int src);

Beispiel: Variant vInteger(4);

__fastcall Variant(const float src);

Beispiel: Variant vSingle(float(5));

__fastcall Variant(const double src);

Beispiel: Variant vDouble(double(6));

__fastcall Variant(const Currency src);

Beispiel: Variant vCurrency(Currency(7));

__fastcall Variant(const TDateTime src);

Beispiel: Variant vDate(TDateTime(String(„02/03/97“)));

__fastcall Variant(const bool src);

Beispiel: Variant vBoolean(true);

__fastcall Variant(const WordBool src);

__fastcall Variant(const Byte src);

Beispiel: Variant vByte(Byte)9);

__fastcall Variant(const AnsiString& src);

__fastcall Variant(const char* src);

__fastcall Variant(wchar_t* const src);

__fastcall Variant(Ole2::IDispatch* const src);

Beispiel: Variant VDispatch(Variant::CreatObject(„Word.Basic“));

__fastcall Variant(Ole2::IUnknown* const src);

Beispiel: Variant vUnknown(Variant::CreatObject                                                     („Word.Basic“).AsType(varUnknown));


Konstruktoren von Variant - Erzeugung per Referenz
Konstruktoren von Variant – Erzeugung per Referenz

Siehe auch

Dies sind die Datentypen, mit denen ein Datentyp der Klasse Variant erzeugt werden kann. Abhängig vom übergebenen Argument verwendet der Compiler den Konstruktor für die Erzeugung per Wert oder per Referenz.

__fastcall Variant(short* src);

Beispiel:

static short int = 3;

Variant vrSmallint = &smallint;

__fastcall Variant(int* src);

Beispiel:

static int integer = 4;

Variant vrInteger = &integer;

__fastcall Variant(float* src);

Beispiel:

static float flt = 5;

Variant vrSingle = &flt;

__fastcall Variant(double* src);

Beispiel:

static double dbl = 6;

Variant vrDouble = &dbl;

__fastcall Variant(Currency* src);

Beispiel:

static Currency currency = 7;

Variant vrCurrency = &currency;

__fastcall Variant(TDateTime* src);

Beispiel:

static TDateTime date = String(„02/03/97“);

Variant vrDate = &date;

__fastcall Variant(WordBool* src);

Beispiel:

static WordBool boolean = true;

Variant vrBoolean = &boolean;

__fastcall Variant(Byte* src);

Beispiel:

static Byte byte = 9;

Variant vrByte = &byte;

__fastcall Variant(wchar_t** src);

Beispiel:

static wchar_t* oleStr = SysAllocString(L“Willkommen in der Welt der Varianten und von OLE.“);

Variant vrOleStr = &oleStr;


Elementfunktion AsType

Beispiel

Syntax

Variant __fastcall AsType(int VarType) const;

Beschreibung

Mit der Funktion AsType wird ein Objekt der Klasse Variant in den Typ umgewandelt, den das Argument VarType vorgibt. Nach dem Aufruf von AsType gibt es zwei Variant-Objekte desselben Typs.


Elementfunktion ChangeType

Siehe auch	Beispiel

Syntax

Variant& __fastcall ChangeType(int VarType);

Beschreibung

Mit der Funktion ChangeType wird der Datentyp eines Objekts der Klasse Variant in den eines Objekts umgewandelt, das bereits instantiiert ist. Nach dem Aufruf von ChangeType gibt es ein Variant-Objekt des Typs VarType.


Beispiel für AsType

// Erzeugung per Wert

Variant vSmallint(short(3));

// Standardkonstruktor verwenden

Variant vx;

vx = vSmallint.AsType(varString);


Beispiel für ChangeType

Variant vx, vy;

cout << endl << "TestTypeChanging output:" << endl;

vx.ChangeType(varInteger);

vy = vx.AsType(varSingle);


Elementfunktion Clear

Siehe auch	Beispiel

Syntax

void __fastcall Clear();

Beschreibung

Nach dem Aufruf von Clear hat die Variante den Wert Unassigned.


Elementfunktion CreateObject

Siehe auch	Beispiel

Syntax

static Variant __fastcall CreateObject(const String& ProgID);

Beschreibung

CreateObject instantiiert eine einzelne Instanz eines OLE-Automatisierungsobjekts.

CreateObject erzeugt ein einzelnes, nicht initialisiertes Objekt der zugehörigen Klasse mit der ProgID, die der Parameter ProgID vorgibt. CreateObject wird verwendet, um ein Objekt des angegebenen Typs zu erzeugen, wenn die ProgID bekannt ist und wenn sich das Objekt auf einem lokalen oder einem In-Process-Server befindet.

CreateObject wird ein einziges Mal aufgerufen, um eine einzelne Klasseninstanz zu erzeugen. Wenn mehrere Instanzen derselben Klasse benötigt werden, empfiehlt es sich, ein iteratives Konstrukt zu verwenden.

CreateObject gibt eine Referenz auf den Schnittstellenbezeichner zurück, der die Kommunikation mit dem Objekt übernimmt. Für CreateObject hat diese Schnittstelle den Typ IDispatch.

Wenn ProgId ungültig ist, löst CreateObject eine EOleSysError-Exception aus.


Elementfunktion Exec

Beispiel

Syntax

Variant __fastcall Exec(AutoCmd& cmd, Integer lcid = LOCALE_SYSTEM_DEFAULT);

Deklaration

Der Standardwert für lcid ist LOCALE_SYSTEM_DEFAULT. Alle Versuche, diesen Wert zu ändern, werden ignoriert. Der Wert wird immer auf LOCALE_SYSTEM_DEFAULT zurückgesetzt.


Beispiel für Exec

Variant   wordBasic, res;

  Function  fileNew("FileNew");

  Procedure insert("Insert");

  Function  fileSaveAs("FileSaveAs");

  cout << endl << "TestOle:  Inhalt von test.txt prüfen" << endl;

  wordBasic = Variant::CreateObject("Word.Basic");

  res = wordBasic.Exec(fileNew <<"Normal");

  wordBasic.Exec(insert <<"Dies ist die erste Zeile\n");

  wordBasic.Exec(insert.ClearArgs() <<"Dies ist die zweite Zeile\n");

  res = wordBasic.Exec(fileSaveAs <<"test.txt" <<3);


Beispiel für CreateObject

Variant   wordBasic, res;

wordBasic = Variant::CreateObject("Word.Basic");

res = wordBasic.Exec(fileNew <<"Normal");

wordBasic.Exec(insert <<" Dies ist die erste Zeile\n");

wordBasic.Exec(insert.ClearArgs() <<" Dies ist die zweite Zeile\n");

res = wordBasic.Exec(fileSaveAs <<"test.txt" <<3);


Elementfunktion GetActiveObject

Siehe auch

Syntax

#include <vcl\sysdefs.h>

static Variant __fastcall GetActiveObject(const String& ProgID);

Beschreibung

GetActiveObject liefert für ein aktuell aktives, registriertes OLE-Objekt eine Referenz auf eine IDispatch-Schnittstelle..

GetActiveObject gibt eine Referenz auf eine IDispatch-Schnittstelle des aktiven Objekts für die Klasse zurück, die mit ProgId bezeichnet ist. ProgId ist die programminterne ID für die Klasse des aktiven Objekts aus der OLE-Registrierdatenbank.

Wenn weder das Klassenobjekt noch die IDispatch-Schnittstelle zurückgegeben werden können, wird eine EOleSysError-Exception ausgelöst.


Elementfunktion OleFunction

Siehe auch

Syntax

#include <vcl\sysdefs.h>

Variant OleFunction(const String& name, Variant& v0 = Variant(),

Variant& v1 = Variant(),Variant& v2 = Variant(),Variant& v3 = Variant(),

Variant& v4 = Variant(),Variant& v5 = Variant(),Variant& v6 = Variant(),

Variant& v7 = Variant(),Variant& v8 = Variant(),Variant& v9 = Variant());

Beschreibung

OleFunction bietet eine alternative Syntax für die OLE-Automatisierung.

Über diese Funktion wird eine Objektfunktion aufgerufen, die einen anderen Rückgabewert als void besitzt.

Benannte Parameter werden nicht unterstützt.


Elementfunktion OleProcedure

Siehe auch

Syntax

void OlePropertySet(const String& name, Variant& v0 = Variant(),

Variant& v1 = Variant(),Variant& v2 = Variant(),Variant& v3 = Variant(),

Variant& v4 = Variant(),Variant& v5 = Variant(),Variant& v6 = Variant(),

Variant& v7 = Variant(),Variant& v8 = Variant(),Variant& v9 = Variant());

Beschreibung

Die Funktion OleProcedure bietet eine alternative Syntax für die OLE-Automatisierung.

Rufen Sie mit dieser Funktion die Funktion eines OLE-Objekts auf, die void zurückgibt.

Benannte Parameter werden nicht unterstützt.


Elementfunktion OlePropertyGet

Siehe auch

Syntax

Variant OlePropertyGet(const String& name, Variant& v0 = Variant(),

Variant& v1 = Variant(),Variant& v2 = Variant(),Variant& v3 = Variant(),

Variant& v4 = Variant(),Variant& v5 = Variant(),Variant& v6 = Variant(),

Variant& v7 = Variant(),Variant& v8 = Variant(),Variant& v9 = Variant());

Beschreibung

Die Funktion OlePropertyGet ermöglicht den Lesezugriff auf eine Eigenschaft eines Automatisierungsobjekts.


Elementfunktion OlePropertySet

Siehe auch

Syntax

void OlePropertySet(const String& name, Variant& v0 = Variant(),

Variant& v1 = Variant(),Variant& v2 = Variant(),Variant& v3 = Variant(),

Variant& v4 = Variant(),Variant& v5 = Variant(),Variant& v6 = Variant(),

Variant& v7 = Variant(),Variant& v8 = Variant(),Variant& v9 = Variant());

Beschreibung

Die Funktion OlePropertySet ermöglicht den Schreibzugriff auf eine Eigenschaft eines Automatisierungsobjekts.


Elementfunktion Type

Siehe auch

Syntax

int __fastcall Type() const;

Beschreibung

Type gibt in codierter Form den Typ einer Variant-Instanz zurück.

Der Rückgabewert wird aus Konstanten gebildet, die in der Header-Datei VCL\SYSDEFS.H deklariert sind.

Rückgabewert

Die folgende Tabelle beschreibt die Bedeutung der einzelnen Typencodes.

Typ	Rückgabewert	Inhalt der Variante

varEmpty    = 0X0000; 	Die Variante hat den Wert Unassigned.

varNull     = 0X0001; 	Die Variante hat den Wert Null.

varSmallint = 0X0002; 	16-Bit-Integer mit Vorzeichen (Smallint).

varInteger  = 0X0003; 	32-Bit-Integer (Integer).

varSingle   = 0X0004; 	Gleitkommawert mit einfacher Genauigkeit (Single).

varDouble   = 0X0005; 	Gleitkommawert mit doppelter Genauigkeit (Double).

varCurrency = 0X0006; 	Gleitkomma-Währungsbetrag (Currency).

varDate     = 0X0007; 	Datums-/Uhrzeitwert (TDateTime).

varOleStr   = 0X0008; 	Referenz auf einen dynamischen Unicode-String.

varDispatch = 0X0009; 	Referenz auf ein OLE-Automatisierungsobjekt (einen Schnittstellenzeiger IDispatch).

varError    = 0X000A; 	Fehlercode des Betriebssystems.

varBoolean  = 0X000B; 	Boolescher 16-Bit-Wert (WordBool).

varVariant  = 0X000C;

varUnknown  = 0X000D; 	Referenz auf ein unbekanntes OLE-Objekt (Zeiger auf IUnknown-Schnittstelle).

varByte     = 0X0011;

varString   = 0X0100; 	Referenz auf einen dynamischen String (AnsiString).

varTypeMask = 0X0FFF;

varArray    = 0X2000;

varByRef    = 0X4000;

Der Rückgabewert von Type entspricht dem Datenelement vType von TVarData.

Der Typ einer Varianten kann mit der Elementfunktion ChangeType geändert werden.


OLE-Automatisierung

Siehe auch

Die OLE-Automatisierung ist ein Mechanismus, über den sich Windows-Anwendungen gegenseitig beeinflussen können. Sie ist in etwa mit einer gemeinsamen Makro-Sprache vergleichbar. Anwendungen, für die eine OLE-Automatisierung implementiert werden kann, werden Automatisierungsobjekte oder Automatisierungs-Server genannt. Eine Anwendung, die eine andere Anwendung automatisiert, heißt Automatisierungs-Controller bzw. Automatisierungs-Client. Die OLE-Automatisierung basiert auf einem Protokoll, über das die Aktionen einer Anwendung von einer anderen Anwendung gesteuert werden können.

C++Builder bietet die volle Unterstützung der OLE 2.0-Automatisierung von Anwendungen. Sie können eine in C++Builder programmierte Anwendung einsetzen, um eine andere Anwendung zu automatisieren oder die Anwendung als OLE-Automatisierungs-Server konfigurieren.

In den Verzeichnissen EXAMPLES\APPS\AUTOCON und EXAMPLE\APPS\AUTOSRV Ihrer C++Builder-Installation finden Sie komplette Beispiele für OLE-Automatisierungs-Server und -Clients.

In der vorliegenden Datei wird nicht im Detail auf die technischen Grundlagen des OLE-Automatisierungssystems von Windows eingegangen. Sie finden hier aber alle Informationen, die Sie zur Durchführung der gängigsten Automatisierungsaufgaben mit C++Builder benötigen:

Eine andere Anwendung automatisieren

OLE-Automatisierungs-Server

OLE-Automatisierungsobjekte

Einen OLE-Automatisierungs-Server erstellen


Eine andere Anwendung automatisieren

Siehe auch	Beispiel

C++Builder-Anwendungen können andere Anwendungen automatisieren, die OLE-Automatisierungs-Server darstellen. Eine Anwendung kann als OLE-Automatisierungs-Server fungieren, wenn sie über OLE-Objekte eine Laufzeitschnittstelle bereitstellt. C++Builder-Anwendungen greifen über die C++Builder-Klasse Variant auf diese OLE-Objekte zu.

Die Automatisierung einer OLE-Anwendung umfaßt drei Arbeitsschritte:

Erstellen der OLE-Objektinstanz

Festlegen der OLE-Objekteigenschaften

Aufrufen der OLE-Objektmethoden

Die OLE-Objektinstanz erstellen

Für jeden OLE-Automatisierungs-Server existiert in der Registrierdatenbank ein Schlüssel namens ProgID, der den Server für die Clients identifiziert. Für die Steuerung dieses Objekts muß der Automatisierungs-Client eine Instanz des Automatisierungsobjekts basierend auf seiner ProgID erzeugen. Zur Erstellung einer OLE-Objektinstanz rufen Sie die Funktion CreateObject auf und weisen deren Rückgabewert einer Variante zu.

CreateObject akzeptiert eine einzelnen String als Parameter. In diesem String wird die ProgID übergeben, die in der Registrierung für das Automatisierungsobjekt enthalten ist. Falls kein Automatisierungsobjekt mit der angegebenen ProgID registriert ist, führt der Aufruf von CreateObject zu einer Exception.

Angenommen, es soll ein OLE-Objekt erstellt werden, mit dem Microsoft Word gesteuert werden kann. Dazu wird eine Variante deklariert und ein OLE-Objekt dafür erzeugt:

Variant MSWord;	// Container für OLE-Objekt deklarieren

MSWord = Variant::CreateObject('Word.Basic'); // Instanz aus der 	// ProgID erzeugen

...	// Objekt verwenden

Zwar sind OLE-Objekte nicht unbedingt Objekte in Sinne von Object Pascal-Objekten und -Klassen, sie können aber mit Hilfe einer ähnlichen Syntax gehandhabt werden. Wenn eine Variante vorliegt, die ein OLE-Objekt enthält, können Sie zum Festlegen der Objekteigenschaften und zum Aufrufen der Methoden des Objekts in etwa so vorgehen wie bei einem "normalen" Objekt.

OLE-Objekteigenschaften festlegen

Viele Automatisierungsobjekte enthalten in ihrer Schnittstelle Eigenschaften, die den Status oder Inhalt des Automatisierungs-Servers repräsentieren.

Die Klasse Variant stellt spezielle Methoden zum Lesen und Schreiben von Automatisierungseigenschaften bereit:

OlePropertyGet(propname)

OlePropertySet(propname,value)	value ist ein beliebiger Wert, der in den Typ Variant konvertiert werden kann.

OLE-Objektmethoden aufrufen

Die meisten Automatisierungsobjekte enthalten in ihrer Schnittstelle Methoden. Wie bei C++Builder-Objekten repräsentieren die Methoden die Aktionen, die mit dem Server ausgeführt werden können.

Die Klasse Variant stellt spezielle Methoden für den Aufruf von Automatisierungsmethoden bereit:

OleProcedure(OleProcName, [val, ...])

OleFunction(OleFuncName, [val, ...])	val ist ein beliebiger Wert, der in den Typ Variant konvertiert werden kann.

Beim Compilieren ist noch nicht bekannt, ob der Name einer Methode bzw. deren Parameter gültig sind, wenn sie zur Laufzeit für das OLE-Objekt aufgerufen wird. Der Compiler faßt deshalb den Methodennamen und die übergebenen Parameter in einem Paket zusammen, das zur Laufzeit verteilt (dispatched) wird.

Alle Parameter werden als Varianten an OLE-Objektmethoden übergeben. Die zurückgelieferten Ergebnisse werden ebenfalls als Varianten interpretiert.


Beispiel

Nachstehend sehen Sie ein einfaches Beispiel, das Daten aus einer Abfrage übernimmt, diese in ein Microsoft Word-Dokument einfügt und den eingefügten Text als Tabelle formatiert. Damit die Automatisierung funktioniert, muß Microsoft Word gestartet und ein Dokument geöffnet sein.

1	Erstellen Sie eine neue Anwendung mit einem leeren Formular.

2	Fügen Sie die Anweisung #include <vlc|OleAuto.hpp> in die Datei UNIT1.CPP ein.

#include <vcl\OleAuto.hpp> // Muß in den Implementierungsbereich eingefügt werden }

3	Fügen Sie eine Abfragekomponente in das Formular ein.

4	Setzen Sie die Eigenschaft DatabaseName des Formulars auf den Alias DCDEMOS.

5	Fügen Sie eine Schaltflächenkomponente in das Formular ein.

6	Verbinden Sie die folgende Behandlungsroutine mit dem Ereignis OnClick der Schaltfläche:

void __fastcall TForm1::Button1Click(TObject *Sender)

{

Vairant MSWord;

int I;

String str;

Query1->SQL->Add("select company, contact, phone from customer"

"where lastinvoicedate >= \"1/1/95""); //SQL-Anweisung für Abfrage

Query1->Open();  //Abfrage für jeden

while (!Query1->Eof)  // Datensatz öffnen

{

for (i=0; i < Query1->FieldCount; I++

str = str + Query1->Fields[i]->AsString = "\t"; // Trennung durch

                                                    // Tabulatoren

Query1->Next(); Nächsten Datensatz abrufen

}

Query1->Close();

MSWord = Variant::CreateObject("word.basic");  //Objekt für 	//Word-Server erstellen

MSWord.OleProcedure("FileNew", "normal");

MSWord.OleProcedure(Insert",str.SubString(1, str.Length()-1));

MSWord.OleProcedure("EditSelectAll");

//-----Für benannte Parameter ist die alternative (Low-Level-) Syntax

//-----erforderlich.

NamedParm cf("ConvertFrom", 1);

NamedParm nc("NumColumns", Query1->FieldCount);

NamedParm fm("Format",0);

NamedParm ap("Apply", 167);

Procedure TextToTable("TextToTable");

MSWord.Exec(TextToTable << cf << nc << fm << ap);

//----Ergebnisse speichern (blockexit schließt Word)-----

MSWord.OleProcedure("FileSaveAs", "d:xxx.doc",3);


OLE-Automatisierungs-Server

Siehe auch

Ein OLE-Automatisierungs-Server ist eine Anwendung oder eine dynamische Link-Bibliothek (DLL), die OLE-Objekte in OLE-Automatisierungs-Clients exportiert.

Es gibt drei wichtige Aspekte bei der Erstellung von Automatisierungs-Servern, die Sie kennen müssen:

In-Process- und Out-of-Process-Server

Automatisierungsobjekte instantiieren

Das Objekt Automation

Ein Beispiel für einen OLE-Automatisierungs-Server und den entsprechenden Client finden Sie in den Verzeichnissen EXAMPLES\APPS\AUTOCON und EXAMPLE\APPS\AUTOSRV.


In-Process- und Out-of-Process-Server

Siehe auch

In Windows werden zwei Arten von OLE-Automatisierungs-Servern unterstützt: In-Process-Server und Out-of-Process-Server (letztere werden auch als lokale Server bezeichnet). Mit C++Builder können beide Server-Arten erstellt werden.

Ein In-Process-Server besteht aus einer dynamischen Link-Bibliothek (DLL), die Automatisierungsobjekte exportiert. Da diese Objekte aus einer DLL stammen, gehören sie zu demselben Windows-Prozeß wie die Client-Anwendung.

In-Process-Server werden sinnvollerweise zur Erstellung von Programmmodulen eingesetzt, auf die unterschiedliche Anwendungen zugreifen, die wiederum verschiedene Sprachen benutzen können. Sie haben den Vorteil, daß sie im gleichen Adreßbereich wie die aufrufende Anwendung ausgeführt werden. Aufrufe des Servers müssen deshalb nicht verschoben werden, was für jeden Aufruf zu einem großen Botschaftsbehandlungs-Overhead führen würde.

Ein Out-of-Process-Server ist eine Standalone-Anwendung, die Automatisierungsobjekte exportiert.


Automatisierungsobjekte instantiieren

Siehe auch

Bestimmte OLE-Automatisierungs-Server können jeweils nur ein einzelnes OLE-Objekt erstellen und exportieren. Andere wiederum unterstützen die Verarbeitung von mehreren OLE-Objekten. Eine dritte Art von OLE-Automatisierungs-Servern kann OLE-Objekte nicht exportieren, sondern nur intern verwenden. Die Beziehung zwischen einem Server und der Anzahl der exportierbaren Objekte wird Instantiierung genannt.

Wenn Sie ein OLE-Automatisierungsobjekt erstellen, legen sie gleichzeitig seine Instantiierung fest. Dadurch weiß Windows, ob eine neue Instanz für den Server angelegt werden muß, wenn eine Instanz für ein Automatisierungsobjekt angefordert wird.

Die folgende Tabelle enthält eine Beschreibung der drei Instantiierungstypen:

Instantiierungstyp	Bedeutung

Intern (Internal)	OLE-Objekte werden von der Anwendung nur intern verwendet. Die OLE-Objekte sind nicht registriert und können deshalb nicht von externen Prozessen erzeugt werden.

Einzeln (Single)	Jede Instanz des Servers kann nur eine Instanz des OLE-Objekts exportieren. Wenn die Clients mehrere Instanzen des OLE-Objekts anfordern, startet Windows jeweils eine neue Instanz des Servers.

Mehrfach (Multiple)	Jede Server-Instanz kann mehrere Instanzen des OLE-Objekts erstellen und exportieren. In-Process-Server (DLLs) sind immer von diesem Typ.


Das Objekt Automation

Siehe auch

Zu jedem Projekt, das die Unit OLEAuto nutzt, gehört ein nicht visuelles Objekt namens Automation. Dieses Objekt kapselt alle Aspekte, welche die OLE-Automatisierung des Server-Projekts betreffen (in etwa so, wie die Komponenten Application und Screen, die in jeder C++Builder-Anwendung erzeugt werden und die Bestandteile der Umgebung kapseln). Die beiden wichtigsten Merkmale des Objekts Automation sind seine Eigenschaft StartMode und sein Ereignis OnLastRelease.

StartMode gibt an, wie und warum der OLE-Server gestartet wurde. Die folgende Tabelle enthält die vier zulässigen Werte für StartMode.

Wert	Bedeutung

smStandAlone	Die Anwendung wurde von einem Benutzer gestartet.

smAutomation	Die Anwendung wurde von Windows zur Erstellung eines OLE-Objekts gestartet.

smRegServer	Die Anwendung wurde nur gestartet, um ein oder mehrere OLE-Objekte zu registrieren.

smUnregServer	Die Anwendung wurde nur gestartet, um die Registrierung eines oder mehrerer OLE-Objekte aufzuheben.

Das Ereignis OnLastRelease gibt an, daß ein Server, der vom System für Automatisierungszwecke gestartet wurde (StartMode ist smAutomation), nicht mehr als Server benötigt wird, da die von ihm erzeugten OLE-Objekte von allen seinen Clients freigegeben wurden. Der Server wird in diesem Fall standardmäßig geschlossen. Sie können aber in einer Behandlungsroutine für das Ereignis OnLastRelease weitere Prüfungen festlegen, die vor dem Schließen des Servers ausgeführt werden sollen. OnLastRelease akzeptiert einen booleschen var-Parameter namens ShutDown, der per Vorgabe den Wert True hat. Durch Setzen von ShutDown auf True verhindern Sie, daß der Server nach der Freigabe des letzten OLE-Objekts geschlossen wird.

Weitere Informationen zur Komponente Automation entnehmen Sie dem Unit-Quelltext von OLEAuto.


OLE-Automatisierungsobjekte

Siehe auch

Alle OLE-Objekte, die von C++Builder-Automatisierungs-Servern exportiert werden, sind von der Klasse TAutoObject abgeleitet. Hinsichtlich dieser Objekte sind einige wichtige Punkte zu beachten:

Rufen Sie nie delete oder einen Zeiger für ein Automatisierungsobjekt auf.

Für OLE-Objekte wird ein Referenzzähler geführt, da sie von mehreren Clients gleichzeitig genutzt werden können. Wenn ein OLE-Objekt nicht mehr benötigt wird, muß die zugehörige Release-Methode aufgerufen werden, damit der Referenzzähler entsprechend dekrementiert wird. Wenn der Zähler dadurch auf Null zurückgesetzt wird, ruft Release delete this auf, um das Objekt freizugeben.

OLE-Objekte werden extern erstellt. Wie bei allen anderen Klassen auch, können Sie aber ein OLE-Objekt durch einen Aufruf von new <class name> auch innerhalb des Servers erzeugen. Wenn das Objekt nicht mehr benötigt wird, rufen Sie aber nicht delete, sondern die Methode Release auf.

OLE-Objekte müssen immer als Varianten exportiert werden. Methoden und Eigenschaften müssen ein OLE-Objekt als Variante zurückgeben, in der das OLE-Objekt enthalten ist. TAutoObject stellt eine Eigenschaft namens OLEObject für diese Zweck bereit. Klassen oder Zeiger auf Klassen dürfen nie außerhalb des Servers exportiert werden. Referenzieren Sie statt dessen immer die Eigenschaft OLEObject des OLE-Objekts, und exportieren sie es als Variante.


Einen OLE-Automatisierungs-Server erstellen

Siehe auch

Unabhängig davon, welche Art von Automatisierungs-Server Sie erstellen, müssen Sie dessen Schnittstelle zu den Clients definieren. Dazu definieren und registrieren Sie das OLE-Objekt und die Automatisierungseigenschaften und -methoden, so daß diese von den Clients referenziert werden können.

Wenn Sie die Schnittstelle eines vorhandenen Automatisierungs-Servers ändern, müssen Sie sicherstellen, daß die Schnittstelle abwärtskompatibel ist. Es dürfen deshalb keine vorhandenen Eigenschaften oder Funktionen entfernt werden. Dies würde zu Fehlern bei den existierenden Clients führen. Generell sollten nur Hinzufügungen zu den bestehenden Schnittstellen durchgeführt werden.

Wenn Sie Änderungen an einer existierenden Schnittstellen durchführen, welche die Abwärtskompatibilität beeinträchtigen, müssen Sie dem Objekt einen anderen Namen geben.

Die Erstellung eines Automatisierungs-Servers umfaßt drei Arbeitsschritte:

Den Automatisierungs-Server erstellen

Ein Automatisierungsobjekt hinzufügen

Automatisierungseigenschaften und -methoden


Den Automatisierungs-Server erstellen

Siehe auch

Im ersten Arbeitsschritt wird der Server selbst erstellt. Dabei legen Sie die Anwendungs- oder die dynamische Link-Bibliothek an, mit der die OLE-Objekte erzeugt und exportiert werden. Die Entscheidung darüber, ob Sie eine DLL oder eine Anwendung erstellen, beruht in erster Linie darauf, ob ein In-Process- oder Out-of-Process-Server benötigt wird.

Die anfänglichen Arbeitsschritte bei der Erstellung dieser Server-Arten unterscheiden sich nicht von der Erstellung einer anderen Anwendung oder DLL. Im weiteren Verlauf der Arbeit kommen jedoch spezielle Vorgehensweisen hinzu.

Folgendermaßen erzeugen Sie einen In-Process-Automatisierungs-Server(DLL) :

1	Legen Sie eine DLL an.

Sie fügen Units und Formulare ebenso wie OLE-Objekte wie gewohnt hinzu.

2	Fügen Sie die Anweisung  #include <vcl\OLEAuto.hpp> nach #pragma hrdstop ein.

3	Exportieren Sie vier Standard-Eintrittspunkte aus der Unit OLEAuto der DLL.

Wenn Sie bereits Elemente aus der DLL exportiert haben, können Sie vier OLE-Automatisierungs-Eintrittspunkte zu der vorhandenen Export-Klausel hinzufügen.

Andernfalls führen Sie die folgenden Schritte aus.

1	Kopieren Sie die Datei BIN\OLEDLL.DEF in Ihr Verzeichnis. Dabei werden die folgenden Eintrittspunkte exportiert, deren Aufruf OLE in einer Automatisierungs-Server-DLL erwartet.

DllGetClassObject

DllCanUnloadNow

DllRegisterServer

DllUnregisterServer

2	Speichern Sie die Datei unter dem Namen des Projekts, allerdings mit der Namenserweiterung DEF.

3	Fügen Sie die Datei mit dem Befehl Projekt/Zum Projekt hinzufügen in das Projekt ein.

Folgendermaßen erstellen Sie einen Out-of-Process-Automatisierungs-Server:

1	Erstellen Sie eine C++Builder-Anwendung.

2	Fügen Sie Automatisierungs-Server-Units hinzu.

Nach der Erstellung des Servers können Sie das OLE-Automatisierungsobjekt hinzufügen.


Automatisierungseigenschaften und -methoden

Siehe auch

OLE-Automatisierungs-Clients bearbeiten OLE-Objekte normalerweise, indem sie deren Methoden aufrufen und Eigenschaften setzen und lesen. Der Server legt fest, welche Eigenschaften und Methoden verfügbar sind. Dazu werden die betreffenden Eigenschaften und Methoden den OLE-Objekten hinzugefügt, die der Server exportiert.

Die Eigenschaften und Methoden eines Automatisierungsobjekts gleichen denjenigen anderer Object Pascal-Klassen. Die wenigen Einschränkungen werden später erläutert.

Wenn Sie der Schnittstelle eines OLE-Objekts eine Eigenschaft oder Methode hinzufügen wollen, deklarieren Sie diese im Abschnitt __automated der Klassendeklaration.

Der Abschnitt __automated einer Klassendeklaration entspricht dem public-Abschnitt, jedoch mit dem Unterschied, daß der Compiler für diese Klasse einen Eintrag in der Automatisierungstabelle anlegt.

Folgende Einschränkungen bestehen für den Code im Abschnitt __automated einer Klassendeklaration:

Es können nur Eigenschaften und Methoden deklariert werden. Felddeklarationen sind nicht erlaubt.

Alle Eigenschafts-, Parameter- und Funktionsergebnistypen, die in Eigenschafts- und Methodendeklarationen verwendet werden, müssen zu einem der folgenden Typen gehören:

SmallInt, int, float, double, Currency, TDateTime, String, WordBool oder Variant.

Eigenschaftsdeklarationen können nur Zugriffsattribute (read und write) enthalten. Andere Attribute (wie index, stored, default, nodefault) sind nicht erlaubt.

Zugriffsattribute müssen einen Methodenbezeichner angeben. Feldbezeichner sind nicht erlaubt.

Eigenschaftzugriffsmethoden müssen die __fastcall-Aufrufkonvention verwenden.

Array-Eigenschaften werden unterstützt.

Das Überschreiben von Eigenschaften (Eigenschaftsdeklarationen, die keinen Eigenschaftstyp aufweisen) ist nicht erlaubt.

Methodendeklarationen müssen die __fastcall-Aufrufkonvention verwenden. Methoden können virtuell, aber nicht dynamisch sein. Das Überschreiben von Methoden ist zulässig.

Eine Eigenschafts- oder Methodendeklaration kann eine optionale dispid-Direktive enthalten, auf die ein Integer-Konstantenausdruck folgen muß. Dieser gibt die Verteiler-ID (dispatch ID) der Eigenschaft oder Methode an.

Ist keine dispid-Klausel vorhanden, nimmt der Compiler automatisch eine Zahl an, die um eins größer ist als die größte Verteiler-ID in einer Eigenschaft oder Methode in der Klasse oder deren Vorfahren.

Die Angabe einer bereits verwendeten Verteiler-ID in einer dispid-Klausel führt zu einem Fehler.


Beispiel für die Referenzierung der Basisklasse

template <class T> class B

{

  // Klassendeklarationen

};

template <class T> class D : public B<T>

{

   // Klassendeklarationen

};

template <class T> void func(B <T> *b)

{

   // Funktionsrumpf

}

// Unter ANSI C++ unzulässig: nicht aufgelöste Funktion func( int )

// Borland C++ ruft jedoch( B<int> * ) auf.

func( new D<int> );


Beispiel für einfache Umwandlungen

template <class T> void func(const T)

{

   .

   .

   .

};

func(0);  // Unter ANSI C++ unzulässig: unaufgelöste Funktion func(int).

// Borland C++ ermöglicht jedoch den Aufruf von func(const int).


Beispiel für eine explizite Funktions-Template

template<class T> T max(T a, T b) {

        return  (a > b) ? a : b;

};

// Deklariert eine explizite Template-Funktion

int     max(int,int);

void    f(int i, char c)

{

        max(i, i);              // Aufruf von max(int ,int )

        max(c, c);              // Aufruf von max(char,char)

        max(i, c);              // Aufruf von max(int,int)

        max(c, i);              // Aufruf von max(int,int)

}


Definition einer Klassen-Template

// Beispiel für die Definition einer Klassen-Template.

template <class T> class Vector

{

   T *data;

   int size;

public:

   Vector(int);

   ~Vector( ) { delete[ ] data; }

   T& operator[ ] (int i) { return data[i]; }

};

// Beachten Sie die Syntax für Out-of-line-Definitionen.

template <class T> Vector<T>::Vector(int n)

{

   data = new T[n];

   size = n;

};

int main()

{

   Vector<int> x(5);   // Generiert einen Vektor zur Speicherung von

                       // fünf Ganzzahlen.

   for (int i = 0; i < 5; ++i)

      x[i] = i;        //  Initialisiert den Vektor.

   return o;

}


Exportierbare/Importierbare Template-Deklarationen

// In der Datei EXPORTER.H

#include<iostream.h>

# if defined (BUILD_DLL_EXPORTS)

#     define DECLSPEC __export

# elif defined (USING_DLL_IMPORTS)

#     define DECLSPEC __import

# endif

/////////////////////////////////////////////////

// Erhält KLASSENDEFINITIONEN

template <class T> class Receive

{

  T value;

public:

  Receive(const T val) : value(val){}

   T display();

};

template<class T> T Receive<T>::display()

{

  return value;

}

// DEFINITION EINER TEMPLATE-FUNKTION

template <class T>

T another_min(T a, T b) { return a < b ? a : b;}

#if (defined (BUILD_DLL_EXPORTS) ||  defined(USING_DLL_IMPORTS) )

////// INSTANTIIERTE KLASSEN-TEMPLATE /////

template class DECLSPEC Receive<double>;

template class DECLSPEC Receive<int>;

template class DECLSPEC Receive<char>;

////// INSTANTIIERTE TEMPLATE-FUNKTIONEN /////

template int DECLSPEC another_min<int>(int, int);

template double DECLSPEC another_min<double>(double, double);

#endif


Compilierung exportierbarer Templates

// In der Datei DLL_SRC.CPP.

// GENERIERT QUELLTEXT FÜR EXPORTIERBARE KLASSEN UND FUNKTIONEN.

// VERWENDEN SIE ZUR COMPILIERUNG DIESER DATEI BCC32 -tWD -DBUILD_DLL_EXPORTS

// DLL_SRC.CPP

#define STRICT

#include <windows.h>

#include "exporter.h"

BOOL WINAPI DllEntryPoint(HINSTANCE hinstdll,

                          DWORD fdwReason, LPVOID lpvReserved)

{

  return 1;

}


Verwendung von Import-Templates

Programmausgabe

// Vor der Compilierung dieser Datei müssen Sie die dynamische Link-

// Bibliothek anlegen. Dazu können Sie den Befehl IMPLIB DLL_SRC.LIB

// DLL_SRC.DLL verwenden.

// BENUTZEN SIE ZUR COMPILIERUNG DIESER DATEI BCC32 -DUSING_DLL_IMPORTS MAIN

// DLL_SRC.LIB

#include <iostream.h>

#include "exporter.h"

int main () {

   int small = 5;

   int big = 10;

   double smalld = 1.2;

   double bigd = 12.3;

   // Für diese Objekte wurde kein neuer Quelltext generiert.

   Receive <double> Test_d(0.01);

   Receive <int> Test_i(5);

  // Generiert Quelltext für dieses Objekt in MAIN.OBJ.

  Receive <float> Test_f(3.14);

   cout << "Test_d.display() = " <<  Test_d.display() << endl;

   cout << "Test_i.display() = " <<  Test_i.display() << endl;

   cout << "min(5, 10): " << another_min(small, big) << endl;

   cout << "min(12.3, 1.2): " << another_min(bigd, smalld)<<endl;

   cout << "Test_f.display() = " <<  Test_f.display() << endl;

   return 0;

}


Programmausgabe

Test_d.display() = 0.01

Test_i.display() = 5

min(5, 10): 5

min(12.3, 1.2): 1.2

Test_f.display() = 3.14


// Beispiel für die Operatoren new und delete

// ZUTEILEN, INITIALISIEREN UND LÖSCHEN EINES ZWEIDIMENSIONALEN

// SPEICHERBEREICHS.

#include <except.h>

#include <iostream.h>

void display(long double **);

void de_allocate(long double **);

int m = 3;                               // ANZAHL DER ZEILEN.

int n = 5;                               // ANZAHL DER SPALTEN.

int main(void) {

   long double **data;

   try {                                 // PRÜFUNG AUF EXCEPTIONS.

      data = new long double*[m];        // SCHRITT 1: ZEILEN EINRICHTEN.

      for (int j = 0; j < m; j++)

          data[j] = new long double[n];  // SCHRITT 2: SPALTEN EINRICHTEN.

      }

   catch (xalloc) {  // DIESEN BLOCK NUR DURCHLAUFEN, WENN xalloc EINTRITT.

      // VOR DEM BEENDEN KÖNNTEN SIE NOCH ANDERE AKTIONEN DURCHFÜHREN.

      cout << "Could not allocate. Bye ...";

      exit(-1);

      }

   for (int i = 0; i < m; i++)

      for (int j = 0; j < n; j++)

          data[i][j] = i + j;            // IRGENDEINE INITIALISIERUNG

   display(data);

   de_allocate(data);

   return 0;

   }

void display(long double **data) {

   for (int i = 0; i < m; i++) {

        for (int j = 0; j < n; j++)

             cout << data[i][j] << " ";

       cout << "\n" << endl;

       }

   }

void de_allocate(long double **data) {

   for (int i = 0; i < m;  i++)

       delete[] data[i];      // SCHRITT 1: SPALTEN LÖSCHEN

   delete[] data;             // SCHRITT 2: ZEILEN LÖSCHEN

   }


Beispiel für die Plazierungssyntax für den Operator new

// Beispiel für die Plazierungssyntax für new()

#include <iostream.h>

class Alpha {

  union {

    char  ch;

    char  buf[10];

    };

public:

   Alpha(char c = '\0') : ch(c) {

      cout << "character constructor" << endl;

      }

   Alpha(char *s) {

      cout << "string constructor" << endl;

      strcpy(buf,s);

      }

   ~Alpha( ) { cout << "Alpha::~Alpha() " << endl; }

   void * operator new(size_t, void * buf) {

      return buf;

      }

};

void main() {

   char *str = new char[sizeof(Alpha)];

   // Plazieren von 'X' am Anfang von str.

   Alpha* ptr = new(str) Alpha('X');

   cout << "str[0] = " << str[0] << endl;

   // Expliziter Destruktoraufruf

   ptr -> Alpha::~Alpha();

   // Plazieren eine Strings im Puffer str.

   ptr = new(str) Alpha("my string");

   cout << "\n str = " << str << endl;

   // Expliziter Destruktoraufruf

   ptr -> Alpha::~Alpha();

   delete[] str;

   }

Ausgabe

character constructor

str[0] = X

Alpha::~Alpha()

string constructor

 str = my string

Alpha::~Alpha()


Beispiel für das Überladen der Operatoren new und delete

#include <stdlib.h>

class X {

   .

   .

   .

public:

   void* operator new(size_t size) { return newalloc(size);}

   void operator delete(void* p) { newfree(p); }

   X() { /* initialize here */ }

   X(char ch) { /* and here */ }

   ~X() { /* clean up here */ }

   .

   .

   .

};


Beispiel

// Die Funktion MakeStr arbeitet mit einem "const-Array"

AnsiString MakeStr(const TVarRec* args, int argHigh)

{

  String tmp;

  for (int i = 0; i <= argHigh; i++)

  switch (args[i].VType)

  {

    case vtInteger:

      tmp += ::IntToStr(args[i].VInteger);

      break;

    case vtBoolean:

      tmp += args[i].VBoolean? "T": "F";

      break;

    case vtChar:

      tmp += String(&args[i].VChar, 1);

      break;

    case vtExtended:

      tmp +=  ::FloatToStr(*args[i].VExtended);

      break;

    case vtString:

      tmp += *args[i].VString;

      break;

    case vtPChar:

      tmp += String(args[i].VPChar);

      break;

    case vtObject:

      tmp += "-TObject-"; //classname not available args[i].VObject->ClassName();

      break;

    case vtClass:

      tmp += "-TClass-"; //classname not available args[i].VClass->ClassName();

      break;

    case vtAnsiString:

      tmp += String(reinterpret_cast<char*>(args[i].VAnsiString));

      break;

    case vtCurrency:

      tmp += ::CurrToStr(*args[i].VCurrency);

      break;

    case vtVariant:

      tmp += *args[i].VVariant;

      break;

    default:

      tmp += "-Other-";

  };

  return tmp;

}

// MakeStr wird aufgerufen mit OPENARRAY(TVarRec, (...))

  ::MessageBox(0,

    MakeStr(OPENARRAY(TVarRec,(1, true, 'a', (long double)3.14159,

    ShortString("shstr"), "hello", *this, __classid(TForm1), String("str"),

    Currency(1.23), Variant("var")))).c_str(), PChar("MakeStr"), MB_OK);

// Das resultierende Meldungsfenster enthält:

//  1Ta3.14159shstrhello-TObject--TClass-str1.23var


Beispiel für die Verwendung von Varianten

#include <vcl.h>

#pragma hdrstop

#include <iostream.h>

#include <oleauto.hpp>

#include <math.h>

//--------------------------------------------------------------------

void TestConstructors();

void TestAssignments();

void TestTypeChanging();

void TestOperators();

void TestArrays();

void TestOle();

//--------------------------------------------------------------------

main()

{

    TestConstructors();

    TestAssignments();

    TestTypeChanging();

    TestOperators();

    TestArrays();

    TestOle();

    return 0;

}

//--------------------------------------------------------------------

void CheckType(const Variant& v, Word type)

{

  if (v.VType == type)

    cout << "Type Check OK: ";

  else  cout << "Type Check ERROR: ";

  cout <<   "type: " << v.VType;

  cout << ", expected: " << type << endl;

}

//--------------------------------------------------------------------

void Compare(Variant &lhs, Variant &rhs)

{

  if (lhs == rhs)

    cout << "Compare OK: ";

  else

    cout << "Compare ERROR: ";

  cout <<   "lhs type: " << lhs.VType;

  cout << ", rhs type: " << rhs.VType;

  cout << ", lhs val: "  << String(lhs).c_str();

  cout << ", rhs val: "  << String(rhs).c_str() << endl;

}

//--------------------------------------------------------------------

void TestConstructors()

{

  static short smallint = 3;

  static int integer = 4;

  static float flt = 5;

  static double dbl = 6;

  static Currency currency = 7;

  static TDateTime date = String("05/11/95");

  static WordBool boolean = true;

  static Byte byte = 9;

  static wchar_t* oleStr = SysAllocString(L"Hey, welcome to the world of variant and OLE");

  Variant vEmpty;

  //can't actually construct a null

  Variant vNull;

  vNull.ChangeType(varNull);

  // by value

  Variant vSmallint(short(3));

  Variant vInteger(4);

  Variant vSingle(float(5));

  Variant vDouble(double(6));

  Variant vCurrency(Currency(7));

  Variant vDate(TDateTime(String("05/11/95")));

  Variant vBoolean(true);

  Variant vByte((Byte)9);

  Variant vString(String("This is an ANSI string, buddy!"));

  Variant vOleStr(L"Hey, welcome to the world of Variants & OLE Automation");

  Variant vDispatch(Variant::CreateObject("Word.Basic"));

  Variant vUnknown(Variant::CreateObject("Word.Basic").AsType(varUnknown));

  // by reference

  Variant vrSmallint = &smallint;

  Variant vrInteger = &integer;

  Variant vrSingle = &flt;

  Variant vrDouble = &dbl;

  Variant vrCurrency = &currency;

  Variant vrDate = &date;

  Variant vrBoolean = &boolean;

  Variant vrByte = &byte;

  Variant vrOleStr = &oleStr;

  cout << endl << "TestConstructors output:" << endl;

  CheckType(vEmpty, varEmpty);

  CheckType(vNull, varNull);

  cout << endl << "TestConstructors(byval) output:" << endl;

  CheckType(vSmallint, varSmallint);

  CheckType(vInteger, varInteger);

  CheckType(vSingle, varSingle);

  CheckType(vDouble, varDouble);

  CheckType(vCurrency, varCurrency);

  CheckType(vDate, varDate);

  CheckType(vBoolean, varBoolean);

  CheckType(vByte, varByte);

  CheckType(vString, varString);

  CheckType(vOleStr, varOleStr);

  CheckType(vDispatch, varDispatch);

  CheckType(vUnknown, varUnknown);

  cout << endl << "TestConstructors(byref) output:" << endl;

  CheckType(vrSmallint, varSmallint | varByRef);

  CheckType(vrInteger, varInteger | varByRef);

  CheckType(vrSingle, varSingle | varByRef);

  CheckType(vrDouble, varDouble | varByRef);

  CheckType(vrCurrency, varCurrency | varByRef);

  CheckType(vrDate, varDate | varByRef);

  CheckType(vrBoolean, varBoolean | varByRef);

  CheckType(vrByte, varByte | varByRef);

  CheckType(vrOleStr, varOleStr | varByRef);

  SysFreeString(oleStr);

}

//--------------------------------------------------------------------

void TestAssignments()

{

  Variant vEmpty, vNull;

  //by val

  Variant vSmallint, vInteger, vSingle, vDouble, vCurrency, vDate;

  Variant vOleStr, vDispatch, vBoolean, vByte, vString, vUnknown;

  //by ref

  Variant vrSmallint, vrInteger, vrSingle, vrDouble, vrCurrency, vrDate;

  Variant vrOleStr, vrDispatch, vrBoolean, vrByte, vrString, vrUnknown;

  static short smallint = 3;

  static int integer = 4;

  static float flt = 5;

  static double dbl = 6;

  static Currency currency = 7;

  static TDateTime date = String("05/11/95");

  static WordBool boolean = true;

  static Byte byte = 9;

  static wchar_t* oleStr = SysAllocString(L"Hey, welcome to the world of variant and OLE");

  vEmpty = Variant();

  vNull = Variant().ChangeType(varNull);

  //by value

  vSmallint = (short)3;

  vInteger = 4;

  vSingle = (float)5;

  vDouble = (double)6;

  vCurrency = Currency(7);

  vDate = (TDateTime) StrToDate(String("03/27/77"));

  vBoolean = true;

  vByte = (Byte)9;

  vString = "This is a Ansi string";

  vOleStr = L"This is an OLE string";

  vDispatch = Variant::CreateObject("Word.Basic");

  vUnknown = Variant::CreateObject("Word.Basic").AsType(varUnknown);

  // by reference

  vrSmallint = &smallint;

  vrInteger = &integer;

  vrSingle = &flt;

  vrDouble = &dbl;

  vrCurrency = &currency;

  vrDate = &date;

  vrBoolean = &boolean;

  vrByte = &byte;

  vrOleStr = &oleStr;

  cout << endl << "TestAssignments output:" << endl;

  CheckType(vEmpty, varEmpty);

  cout << endl << "TestAssignments (by val) output:" << endl;

  CheckType(vSmallint, varSmallint);

  CheckType(vInteger, varInteger);

  CheckType(vSingle, varSingle);

  CheckType(vDouble, varDouble);

  CheckType(vCurrency, varCurrency);

  CheckType(vDate, varDate);

  CheckType(vBoolean, varBoolean);

  CheckType(vByte, varByte);

  CheckType(vString, varString);

  CheckType(vOleStr, varOleStr);

  CheckType(vDispatch, varDispatch);

  CheckType(vUnknown, varUnknown);

  cout << endl << "TestAssignments (by ref) output:" << endl;

  CheckType(vrSmallint, varSmallint | varByRef);

  CheckType(vrInteger, varInteger | varByRef);

  CheckType(vrSingle, varSingle | varByRef);

  CheckType(vrDouble, varDouble | varByRef);

  CheckType(vrCurrency, varCurrency | varByRef);

  CheckType(vrDate, varDate | varByRef);

  CheckType(vrBoolean, varBoolean | varByRef);

  CheckType(vrByte, varByte | varByRef);

  CheckType(vrOleStr, varOleStr | varByRef);

  SysFreeString(oleStr);

}

//--------------------------------------------------------------------

void TestTypeChanging()

{

  Variant vSmallint = 3;

  Variant vx, vy;

  cout << endl << "TestTypeChanging output:" << endl;

  vx = vSmallint.AsType(varString);

  CheckType(vx, varString);

  vx.ChangeType(varInteger);

  CheckType(vx, varInteger);

  vy = vx.AsType(varSingle);

  CheckType(vy, varSingle);

  CheckType(vx, varInteger);

  vx.Clear();

  CheckType(vx, varEmpty);

}

//--------------------------------------------------------------------

void TestOperators()

{

  Variant vx, vy;

  cout << endl <<"TestOperators output:" << endl;

  vx = 5;

  vy = vx + 0;

  Compare(vy, vx);

  Compare(vx-0, vx);

  Compare(vx*1, vx);

  Compare(vx/1, vx);

  vx = 5;

  vy = vx + 0;

  Compare(vy, vx);

  Compare(vx-0, vx);

  Compare(vx*1, vx);

  Compare(vx/1, vx);

  vy = vx;

  Compare(vy+=0, vx);

  Compare(vy-=0, vx);

  Compare(vy*=1, vx);

  Compare(vy/=1, vx);

  vx = 5;

  vy = 46;

  vx = 2*vx + (vy-6)/2;

  Compare(vx, 30);

  vx = "hi ";

  vy = "there";

  vx += vy;

  Compare(vx, "hi there");

  //force a variant so we can test variant's operator+

  vx = 1 + 20.0 + Variant(String("300"));

  Compare(vx, 321);

  vx = 15;

  vy = (vx == 15);

  Compare(vy, true);

  vy = (vx < 15);

  Compare(vy, false);

  vy = (vx > 15);

  Compare(vy, false);

  vy = (vx != 15.3);

  Compare(vy, true);

}

//--------------------------------------------------------------------

void TestArrays()

{

  Variant a, b;

  int i, tmp;

  cout << endl <<"TestArrays output:" << endl;

  a = VarArrayCreate(OPENARRAY(int, (0, 9)), varInteger);

  b = VarArrayCreate(OPENARRAY(int, (1, 3, 0, 9)), varInteger);

  // Do it with array lock

  int* ax = (int*) a.ArrayLock();

  for (i=0; i < 10; i++)

    ax[i] = i*i;

  Compare(ax[0], 0);

  Compare(ax[9], 81);

  a.ArrayUnlock();

  //Do it again with Put/GetElement

  for (i=0; i < 10; i++)

  {

    tmp = i*i*i;

    a.PutElement(&tmp, i);

  }

  Compare(a.GetElement(0), 0);

  Compare(a.GetElement(9), 9*9*9);

  // Try a 2-dimensional array

  for (i=0; i < 10; i++)

  {

    b.PutElement(&i, 1, i);

    tmp = sqrt(i);

    b.PutElement(&tmp, 2, i);

    tmp = i*i;

    b.PutElement(&tmp, 3, i);

  }

  Compare(b.GetElement(1,0), 0);

  Compare(b.GetElement(1,9), 9);

  Compare(b.GetElement(2, 0), 0);

  Compare(b.GetElement(2, 9), 3);

  Compare(b.GetElement(3, 0), 0);

  Compare(b.GetElement(3, 9), 81);

}

//--------------------------------------------------------------------

void TestOle()

{

  Variant   wordBasic, res;

  Function  fileNew("FileNew");

  Procedure insert("Insert");

  Function  fileSaveAs("FileSaveAs");

  cout << endl << "TestOle:  check contents of test.txt" << endl;

  wordBasic = Variant::CreateObject("Word.Basic");

  res = wordBasic.Exec(fileNew <<"Normal");

  wordBasic.Exec(insert <<"This is the first line\n");

  wordBasic.Exec(insert.ClearArgs() <<"This is the second line\n");

  res = wordBasic.Exec(fileSaveAs <<"test.txt" <<3);

}